İçeriğe Atla
Mustafa Erbay
Teknoloji · 10 dk okuma · görüntülenme Read in English

Dağıtık Kilit Alternatifleri: Pragmatik Sistem Tasarımı Deneyimlerim

Dağıtık sistemlerde kilit yönetimi, veri tutarlılığı için kritik. Redis, PostgreSQL ve veritabanı kilitleri gibi farklı alternatifleri ve gerçek dünya…

100%

Dağıtık sistemlerde veri tutarlılığını sağlamak her zaman baş ağrısı yaratmıştır. Aynı anda birden fazla servisin aynı kaynağa erişmeye çalışması, çakışmalara ve tutarsız verilere yol açabilir. Bu noktada dağıtık kilitler devreye giriyor; ancak doğru kilit mekanizmasını seçmek, çoğu zaman teknik bir tercih olmaktan öte, uygulamanın iş yüküne, hata toleransına ve hatta bütçeye göre değişen pragmatik bir karara dönüşüyor.

Yirmi yıllık tecrübemde, basit bir UPDATE sorgusundan, üretim ERP’sindeki karmaşık stok hareketlerine kadar birçok farklı senaryoda dağıtık kilitlerle cebelleştim. Burada, bu farklı alternatifleri ve benim bu konudaki deneyimlerimi, hangi durumda neyi tercih ettiğimi ve nedenlerini paylaşacağım.

Giriş: Neden Dağıtık Kilitlere İhtiyaç Duyarız?

Dağıtık sistemlerde, birden fazla sürecin veya servisin aynı anda paylaşılan bir kaynağa (bir dosya, bir veritabanı kaydı, bir envanter bilgisi) erişmesini engellemek için dağıtık kilitlere ihtiyaç duyarız. Bu kilitler, kaynak üzerinde tekil bir kontrol mekanizması sağlayarak veri bozulmalarını veya beklenmedik durumları önler. Örneğin, bir kullanıcının bakiyesinden para çekerken, aynı anda iki farklı işlemin bakiyeyi düşürmesini engellememiz gerekir.

Bunu en net hissettiğim yer, sipariş durumlarını güncelleyen bir akış oldu. Aynı sipariş için hem ödeme onayı hem de kargo hazırlığı aynı anda tetiklendiğinde, sipariş durumu birden fazla kez güncellenip tutarsız bir hale geliyordu. Bunu çözmek için basit bir veritabanı kilitleme mekanizmasına gitmek zorunda kaldım.

Veritabanı Kilitleri: Güvenilir Ama Maliyetli Bir Seçenek

Veritabanları, dağıtık kilitler için doğal bir adaydır. İşlemlerin atomik doğası ve yerleşik kilitleme mekanizmaları sayesinde, veri tutarlılığını garanti altına almak nispeten kolaydır. Özellikle PostgreSQL gibi güçlü veritabanlarında hem satır seviyesi kilitler (SELECT FOR UPDATE) hem de danışma kilitleri (pg_advisory_lock) kullanılabiliyor.

Bir üretim ERP’sinde, stok hareketlerini işlerken SELECT FOR UPDATE kullanmak benim için vazgeçilmezdi. Bir ürünün stok miktarını güncellerken, aynı anda başka bir işlemin o stoğu okuyup yanlış bir karar vermesini engellemem gerekiyordu. Bu, özellikle yüksek hacimli işlemlerde performans darboğazlarına yol açabilse de, veri tutarlılığı kritik olduğunda ödün verilmesi gereken bir maliyetti.

BEGIN;
SELECT stock_quantity FROM products WHERE product_id = 123 FOR UPDATE;
-- stock_quantity'yi oku ve yeni değeri hesapla
UPDATE products SET stock_quantity = new_quantity WHERE product_id = 123;
COMMIT;

pg_advisory_lock ise daha hafif ve uygulama seviyesinde yönetilebilen bir kilit sağlar. Belirli bir arka plan görevinin yalnızca tek bir instance tarafından çalıştırılmasını garantilemek istediğinizde iyi bir araç. Bu kilitler, veritabanı satırlarına bağlı olmadığı için ölü kilitlenme (deadlock) riskini azaltıyor ve daha esnek bir kullanım sunuyor. Ancak, kilitlerin doğru bir şekilde serbest bırakıldığından emin olmak tamamen yazılımcının sorumluluğunda.

Redis Kilitleri: Hız ve Dikkat Edilmesi Gerekenler

Redis, in-memory yapısı sayesinde çok hızlı bir kilit sunar ve SETNX (SET if Not eXists) komutu ile dağıtık kilit uygulamak oldukça yaygındır. Bir kilidi almak için belirli bir anahtarı belirli bir süre (TTL - Time To Live) ile Redis’e yazarsınız. Eğer anahtar zaten varsa, kilit alınamaz.

Kullanıcıların aynı görevi birden fazla kez tetiklemesini engellemek istediğiniz tipik bir senaryoda Redis kilitleri pratik bir çözüm. Kullanıcı bir görevi başlat düğmesine tıkladığında, görevin ID’si ile bir Redis kilidi alır ve işlem tamamlandığında serbest bırakırsınız. Bu sayede, aynı görevin arka planda birden fazla kez çalışması engellenir.

import redis
import uuid

r = redis.Redis(host='localhost', port=6379, db=0)

def acquire_lock(lock_name, acquire_timeout=10, lock_timeout=10):
    identifier = str(uuid.uuid4())
    end = time.time() + acquire_timeout
    while time.time() < end:
        if r.set(lock_name, identifier, ex=lock_timeout, nx=True):
            return identifier
        time.sleep(0.001)
    return False

def release_lock(lock_name, identifier):
    pipe = r.pipeline(True)
    while True:
        try:
            pipe.watch(lock_name)
            if pipe.get(lock_name).decode('utf-8') == identifier:
                pipe.multi()
                pipe.delete(lock_name)
                pipe.execute()
                return True
            pipe.unwatch()
            break
        except redis.exceptions.WatchError:
            pass
    return False

Ancak Redis kilitlerinin bazı zayıf noktaları var. Redlock algoritması bu zayıflıkları gidermek için ortaya atılsa da, network partition durumlarında veya Redis instance’ının çöküp kalktığı senaryolarda sorunlar yaşanabilir. Redis hafıza baskısı altında kalıp anahtarları boşaltmaya başladığında, kilitlerin süresinden önce serbest bırakılması mümkün. Bu yüzden eviction policy’yi noeviction olarak ayarlayıp, Redis’in hafıza limitlerine dikkat etmek gerekiyor.

Basit Dosya Kilitleri ve Diğer Yerel Çözümler

Dağıtık sistemlerin olmadığı, tek bir sunucuda çalışan uygulamalar için dosya kilitleri veya basit mkdir gibi işletim sistemi araçları yeterli olabilir. flock komutu, bir dosya üzerinde tekil bir erişim sağlayarak aynı anda birden fazla sürecin aynı dosyaya yazmasını engeller.

Bir zamanlar, cron job ile çalışan ve belirli raporları oluşturan bir scriptim vardı. Bu scriptin aynı anda iki kez çalışmasını engellemek için basit bir dosya kilidi kullandım. Script başladığında /tmp/rapor_uret.lock adında bir dosya oluşturmaya çalışıyor, eğer dosya zaten varsa çıkıyordu. Bu kadar basit bir çözüm, o senaryo için gayet yeterliydi.

#!/bin/bash
LOCK_FILE="/tmp/my_script.lock"

# Kilidi almaya çalış
if ( set -o noclobber; echo "$$" > "$LOCK_FILE") 2> /dev/null; then
    trap 'rm -f "$LOCK_FILE"; exit $?' INT TERM EXIT
    echo "Script çalışıyor, kilit alındı."
    # Gerçek script mantığı buraya gelir
    sleep 30
    echo "Script bitti."
    rm -f "$LOCK_FILE"
else
    echo "Script zaten çalışıyor. Çıkılıyor."
    exit 1
fi

Kuyruk Sistemleri ile İş Parçalama ve Tekillik

Bazen dağıtık kilitlere ihtiyaç duymak yerine, problemi farklı bir açıyla ele almak daha mantıklı olabilir. Kuyruk sistemleri (Kafka, RabbitMQ gibi), işleri tekil kuyruklara bölerek veya idempotency desenleri kullanarak dağıtık kilit sorununu dolaylı yoldan çözebilir.

Bir bankanın iç platformunda, finansal işlem onaylarını işlerken event-sourcing mimarisi kullandık. Her onay talebi bir kuyruğa düşüyor ve tek bir tüketici (consumer) tarafından işleniyordu. Bu sayede, aynı işlemin birden fazla kez onaylanması riski ortadan kalkıyordu. Ayrıca, her işlem benzersiz bir correlation_id ile işaretlenerek idempotent hale getirildi. İşlem tekrar işlense bile aynı sonucu verecekti.

{
  "event_id": "b3e0c7a1-8d2a-4f5c-9c7d-0a1b2c3d4e5f",
  "correlation_id": "tx-12345-abcde",
  "event_type": "PaymentApprovalRequested",
  "payload": {
    "account_id": "ACC001",
    "amount": 100.00,
    "currency": "TRY"
  },
  "timestamp": "2026-05-25T10:00:00Z"
}

Bu yaklaşım, dağıtık kilitlerin getirdiği koordinasyon yükünü azaltır ve sistemin ölçeklenebilirliğini artırır. Ancak, kuyruk sistemlerinin kendisi de karmaşık bir yapıya sahip olabilir ve doğru bir şekilde yönetilmesi gerekir. Örneğin, bir kuyruk tüketicisinin çökmesi durumunda mesajların yeniden işlenmesi veya hata kuyruklarına yönlendirilmesi gibi senaryoları iyi tasarlamak şart.

Pragmatik Seçimler ve Trade-off’lar

Her dağıtık kilit alternatifinin kendine özgü avantajları ve dezavantajları var. Benim deneyimimde, seçim genellikle uygulamanın kritiklik seviyesine, performans gereksinimlerine ve hata toleransına göre şekillendi.

Kilit Mekanizması Avantajları Dezavantajları Kullanım Senaryosu
Veritabanı Kilitleri Yüksek tutarlılık, ACID garantisi Performans darboğazı, ölü kilitlenme riski Finansal işlemler, kritik envanter güncellemeleri
Redis Kilitleri Hızlı, ölçeklenebilir Network partition, Redis crash riskleri Kısa süreli görevler, rate limiting, önbellek kilitleri
Dosya Kilitleri Basit, kurulumu kolay Dağıtık değil, ölü kilitlenme riski Tek sunuculu cron job’lar, yerel kaynak erişimi
Kuyruk Sistemleri Ölçeklenebilir, idempotency desteği Daha karmaşık mimari, gecikme riski Asenkron işleme, event-driven mimariler

Uzun süren ve aynı anda birden fazla kez tetiklenmemesi gereken ağır bir arka plan işinde ise Redis kilitlerini tercih ederim, çünkü Redis hızlıdır ve işin çalışma süresi öngörülebilir bir aralıkta olduğunda TTL ile yönetmek kolaydır. Ancak, Redis’in yüksek erişilebilirliğini garanti altına almak için Sentinel mimarisi kullanmak gerekir.

Buna karşılık, Redis kilitlerine güvenirken failover veya network partition senaryolarının gözden kaçması ciddi sonuçlar doğurabiliyor: bir Redis düğümünün yeniden başlaması sırasında kilit garantisi bozulursa, aynı kaynağa iki kez erişim mümkün hale gelir. Bu yüzden bu çözümleri kullanırken failover ve network partition davranışını baştan tasarlamak şart.

Sonuç: Dağıtık Kilitlerde Benim Yaklaşımım

Dağıtık kilitler, dağıtık sistemlerin kaçınılmaz bir parçasıdır ve doğru seçildiğinde büyük kolaylıklar sağlar. Ancak her zaman “en iyi” kilit mekanizması diye bir şey yoktur. Uygulamanın ihtiyaçlarını, performans beklentilerini ve hata toleransını göz önünde bulundurarak pragmatik bir seçim yapmak gerekir. Benim kişisel yaklaşımım, mümkün olduğunca veritabanı kilitlerinden kaçınmak, çünkü bunlar genellikle en yavaş olanlarıdır.

Bunun yerine, Redis kilitleri veya kuyruk sistemleri gibi daha hafif ve ölçeklenebilir çözümlere yönelirim. Ancak bu çözümleri kullanırken, olası hata senaryolarını (network partition, sunucu çökmesi, kilit süresinin dolması) her zaman hesaba katıp, bu durumlar için retry mekanizmaları, timeout süreleri ve idempotency prensiplerini uygulamaya dahil ederim. Unutmayalım ki, dağıtık sistemlerde hiçbir şey yüzde yüz garantili değildir, bu yüzden her zaman bir fallback mekanizması veya hatayla başa çıkma stratejisi bulundurmak şarttır.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

Sıkça Sorulanlar

Bu makale ile ilgili okurların sorduğu yaygın sorular.

Dağıtık kilit sistemini ilk kez implemente ederken nelere dikkat etmem gerekir?
Ben ilk kez dağıtık kilit sistemini implemente ederken, sistemdeki tüm bileşenlerin birbirleriyle nasıl etkileşime gireceğini ve hangi kaynakların kilitleme altına alınacağını belirlemek için çok zaman harcadım. Bu süreçte, sistemdeki her bir bileşenin rolünü ve sorumluluklarını iyi anlamak ve kilitleme mekanizmalarının nasıl çalışacağını belirlemek kritikti. Ayrıca, farklı dağıtık kilit alternatiflerini değerlendirirken, her birinin avantajları ve dezavantajlarını dikkatlice incelemem necesario oldu.
Veritabanı kilitleri ile Redis gibi dış kilit hizmetleri arasında seçim yaparken hangi kriterleri göz önünde bulundurmalıyım?
Ben bu seçim yaparken, sistemdeki veri tutarlılığı ve işlemlerin atomik doğasını göz önünde bulundurdum. Veritabanı kilitleri, daha güvenilir ve tutarlı bir seçim gibi görünse de, external kilit hizmetleri gibi Redis, daha esnek ve ölçeklenebilir bir çözüm sunabilir. Ayrıca, sistemdeki yük ve trafik durumu da bu seçimde önemli bir rol oynar. Örneğin, yüksek trafikli bir sistemde, external kilit hizmetleri daha iyi bir performans sunabilir.
Dağıtık kilit sistemini tasararken hata toleransı nasıl sağlanmalıdır?
Ben dağıtık kilit sistemini tasarlarerken, hata toleransı sağlamak için birçok farklı strateji denedim. Örneğin, kilit işlemlerinin timeout sürelerini ayarlamak, kilitleme işlemlerini yeniden deneme mekanizmalarıyla desteklemek ve sistemdeki hataları izlemek için logging mekanizmaları kurmak gibi. Ayrıca, sistemdeki farklı bileşenlerin birbirleriyle nasıl etkileşime gireceğini ve hangi hataların oluşabileceğini düşünmek de çok önemli. Bu şekilde, sistemdeki hataları öngörerek ve önleyici tedbirler alarak, daha güvenilir bir dağıtık kilit sistemi tasarlayabilirsiniz.
Dağıtık kilit sisteminin performansını nasıl ölçülür ve optimize edilir?
Ben dağıtık kilit sisteminin performansını ölçmek için, sistemdeki kilitleme sürelerini, işlemlerin ortalama süresini ve sistemdeki yükü izledim. Ayrıca, sistemdeki farklı bileşenlerin performansını ayrı ayrı inceleyerek, hangi bileşenlerin sistemin genel performansını etkilediğini belirledim. Optimizasyon için, kilitleme mekanizmalarını daha verimli hale getirmek, sistemdeki yükü daha iyi dağıtmak ve gereksiz işlemleri azaltmak gibi stratejiler uyguladım. Örneğin, sistemdeki veri tutarlılığını sağlamak için necesario olan kilitleme işlemlerini azaltarak, sistemdeki genel performansını artırmak mümkün olabilir.
ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Yorumlar

Sunucu Taraflı AI Moderasyon

Yorumlar sunucuda yapay zeka ile denetlenir ve kalıcı olarak saklanır.

?
0/2000

Sunucu taraflı AI denetim

✉️ Ücretsiz · Spam yok · İstediğin an çık

Yeni yazılardan haberdar olun

Yeni içerikler ve teknik notlar e-postanıza gelsin.

  • 📌
    Haftanın en iyisi Sadece okumaya değer tek yazı
  • 🔧
    Alet çantası Bu hafta kullandığım araçlar
  • 🧠
    Perde arkası Blog'a girmeyen notlar

Spam yapmıyoruz. İstediğiniz zaman ayrılabilirsiniz. · Sadece Umami (self-hosted, Google yok) ile takip.

Okuma İstatistikleriniz

0

Yazı Okundu

0dk

Okuma Süresi

0

Gün Serisi

-

Favori Kategori

İlgili Yazılar