Redis ile dağıtık kilit, tek bir Redis örneğinde SET kaynak_adi rastgele_deger NX PX 30000 komutuyla alınır: anahtar yoksa oluşturulur ve 30 saniye sonra kendiliğinden silinir. Kilidi bırakırken yalnızca değer hâlâ sizin rastgele değerinizse silmeniz gerekir; Redis 8.4 ve sonrasında bunu DELEX kaynak_adi IFEQ rastgele_deger yapar, daha eski sürümlerde aynı işi küçük bir Lua betiği görür. Tek örneğin yetmediği, arıza anında bile karşılıklı dışlamanın korunması gereken durumlar için Redis belgeleri birden çok bağımsız Redis örneği üzerinde çalışan Redlock algoritmasını önerir.
Bu yazı resmî "Distributed Locks with Redis" sayfasını temel alır: tek örnekte doğru kilit, neden kopya (replica) ile yedeklemenin yetmediği, Redlock adımları, kalıcılık ve saat sorunları, kilit uzatma ve belgenin tutarlılık uyarıları. SET seçeneklerinin genel anlatımı için string komutları yazısına, anahtar süreleri için EXPIRE ve TTL yazısına bakabilirsiniz. Kilit kullanım senaryolarına genel bakış ise Redis temelleri yazısında yer alır.
Bir dağıtık kilitten ne beklenir?

Farklı süreçlerin ortak bir kaynağı karşılıklı dışlamayla kullanması gerektiğinde dağıtık kilit kullanılır: aynı faturayı iki işçinin aynı anda kesmemesi, aynı dosyanın iki sunucuda birden işlenmemesi gibi. Belge, bir kilidin etkili kullanılabilmesi için gereken en az üç özelliği şöyle tanımlar:
- Güvenlik (karşılıklı dışlama): Herhangi bir anda kilidi yalnızca bir istemci tutabilir.
- Canlılık A (kilitlenmesizlik): Kilidi tutan istemci çökse veya ağdan kopsa bile sonunda kilit yeniden alınabilir hale gelir.
- Canlılık B (hata toleransı): Redis düğümlerinin çoğunluğu ayakta olduğu sürece istemciler kilit alıp bırakabilir.
Kilidin süreli olması canlılık A'yı sağlar: istemci ne olursa olsun anahtar sonunda süresi dolarak silinir. Rastgele değer güvenliği korumanın parçasıdır. Çoğunluk kuralı ise hata toleransını getirir. Aşağıdaki bölümler bu üç parçayı sırayla kurar.
Tek Redis örneğinde doğru kilit
Belgeye göre tek örnekte doğru yöntem, ara sıra bir yarış durumunun kabul edilebildiği uygulamalarda gerçekten kullanılabilir bir çözümdür ve dağıtık algoritmanın da temelidir. Kilidi almak için:
SET resource_name my_random_value NX PX 30000NX, anahtarı yalnızca yoksa oluşturur; PX 30000 ise 30 000 milisaniyelik bir süre koyar. SET komut sayfasının dönüş bilgisine göre anahtar oluşturulursa yanıt OK, anahtar zaten var olduğu için NX koşulu sağlanmazsa yanıt boş (nil) olur. İstemci OK aldıysa kilidi almıştır; nil aldıysa bir süre bekleyip yeniden dener.
Değerin rastgele ve tüm istemciler ile tüm kilit istekleri arasında benzersiz olması şarttır. Belge bunun için /dev/urandom'dan alınan 20 bayt varsayar; daha ucuz bir yol olarak mikro saniye hassasiyetli UNIX zaman damgasını istemci kimliğiyle birleştirmeyi de anar, bunun daha az güvenli olduğunu ama çoğu ortam için muhtemelen yeterli olduğunu ekler. Sabit bir dizge kullanmayın; tüm amaç kilidi kimin aldığını değerden anlayabilmektir.
Kilidi güvenle bırakmak: DELEX veya Lua
Kilidi düz DEL ile silmek güvenli değildir. Şu senaryoyu düşünün: istemci kilidi alır, bir işlem sırasında kilit süresinden daha uzun süre takılır, bu arada anahtarın süresi dolar ve kilidi başka bir istemci alır. Takılan istemci uyanıp DEL çalıştırırsa artık kendisine değil, başka istemciye ait kilidi siler. Çözüm, silmeden önce değerin hâlâ sizin değeriniz olduğunu atomik olarak kontrol etmektir. Redis 8.4 ile gelen DELEX bunu tek komutla yapar:
DELEX resource_name IFEQ my_random_valueDELEX dönüş değeri tamsayıdır: silindiyse 1, anahtar yoksa veya IFEQ koşulu sağlanmadıysa 0. Komut sayfası 8.4'te koşul seçenekleriyle kullanıldığında anahtarın string tipinde olması gerektiğini, aksi halde hata döneceğini belirtir. Daha eski sunucularda belgede verilen Lua betiği aynı mantığı uygular:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
endSET sayfası bu betiğin EVAL ...betik... 1 resource-name token-value biçiminde çağrılacağını söyler: 1 anahtar sayısıdır, ardından anahtar adı ve beklenen değer gelir. Betik Redis içinde atomik çalıştığı için okuma ile silme arasında başka bir istemcinin araya girme şansı yoktur. EVAL, KEYS/ARGV ayrımı ve betiği EVALSHA ile önbellekten çağırma için Lua script yazısına bakın.
Kilit geçerlilik süresi
Anahtara verdiğiniz süreye belge "kilit geçerlilik süresi" der. Bu süre hem kendiliğinden bırakma süresidir hem de istemcinin işini bitirmek için sahip olduğu zamandır; karşılıklı dışlama garantisi kilidin alındığı andan itibaren yalnızca bu pencere boyunca geçerlidir. Pratik sonucu şudur: işin süresi kilit süresini aşabiliyorsa ya süreyi buna göre seçmeli ya da aşağıda anlatılan uzatma mekanizmasını kullanmalısınız. Belge, bu yöntemle tek ve her zaman erişilebilir bir örnekten oluşan dağıtık olmayan bir sistemde akıl yürütmenin güvenli olduğunu söyler.
Kopya (replica) ile yedeklemek neden yetmez?
Tek örnek tek arıza noktasıdır. Akla gelen ilk çözüm bir kopya eklemek ve ana sunucu düşünce kopyaya geçmektir. Belge bunun uygulanabilir olmadığını, çünkü Redis replikasyonunun eşzamansız olduğunu belirtir ve şu yarış durumunu verir:
- İstemci A kilidi ana sunucuda alır.
- Ana sunucu, anahtara yazılan değer kopyaya iletilmeden çöker.
- Kopya ana sunucu olarak yükseltilir.
- İstemci B, A'nın hâlâ tuttuğunu sandığı aynı kaynağın kilidini alır. Güvenlik ihlal edilmiştir.
Belge burada dürüst bir ayrım yapar: arıza anı gibi özel durumlarda birden fazla istemcinin kilidi aynı anda tutması sizin uygulamanız için sorun değilse replikasyon temelli çözümü kullanabilirsiniz. Değilse belgenin önerisi Redlock'tur.
Redlock algoritması

Dağıtık sürümde N adet Redis ana sunucusu olduğu varsayılır. Bu düğümler tamamen bağımsızdır; aralarında replikasyon veya başka bir örtük koordinasyon yoktur. Her düğümde kilit tam olarak yukarıdaki tek örnek yöntemiyle alınıp bırakılır. Belgenin örneğinde N=5'tir ve bu beş sunucunun birbirinden büyük ölçüde bağımsız arızalanması için farklı bilgisayarlarda veya sanal makinelerde çalışması gerekir. İstemci kilidi almak için şu adımları izler:
- Geçerli zamanı milisaniye cinsinden alır.
- Aynı anahtar adı ve aynı rastgele değerle N örneğin hepsinde kilidi paralel olarak almaya çalışır. Her örnek için toplam kilit süresine göre küçük bir zaman aşımı kullanır; belgenin örneğinde 10 saniyelik kilit için bu zaman aşımı 5 ile 50 milisaniye aralığındadır. Böylece erişilemeyen bir düğüm istemciyi uzun süre bekletmez.
- Kilidi almak için geçen süreyi, şimdiki zamandan ilk adımdaki zamanı çıkararak hesaplar. Kilidi örneklerin çoğunluğunda (beşte en az üç) aldıysa ve geçen toplam süre kilit geçerlilik süresinden kısaysa kilit alınmış sayılır.
- Kilit alındıysa geçerlilik süresi, başlangıçtaki süreden geçen sürenin çıkarılmasıyla bulunur.
- Kilit alınamadıysa (N/2+1 örnek kilitlenemediyse veya kalan geçerlilik süresi negatifse) istemci tüm örneklerde kilidi bırakmaya çalışır; kilitleyemediğini düşündüğü örnekler dahil.
Son adımdaki "kilitleyemediğim örneklerde de bırak" kuralı ilk bakışta gereksiz görünebilir, ama bir örneğe gönderilen SET başarılı olup yanıtı zaman aşımına uğramış olabilir. Bırakma işlemi zaten "değer benimse sil" mantığıyla çalıştığı için kilitlenmemiş bir örnekte bırakma denemesi zarar vermez.
Mini örnek: geçerlilik süresi hesabı
Kilit süresi 10 000 milisaniye olsun. İstemci t=0 anında zamanı okur, beş düğüme paralel istek gönderir ve dördünden OK alır; son yanıt t=40 milisaniyede gelir. Çoğunluk (en az üç) sağlandığı ve geçen 40 milisaniye 10 000'den az olduğu için kilit alınmıştır. Kalan geçerlilik süresi 10 000 - 40 = 9 960 milisaniyedir; belge saat kayması için bundan birkaç milisaniye daha düşmeyi önerir. İstemci işini bu sürenin içinde bitirmezse karşılıklı dışlama garantisi ortadan kalkar. Aynı senaryoda yalnızca iki düğümden OK gelseydi istemci kilidi almamış sayılır ve beş düğümün hepsinde bırakma komutunu gönderirdi.
Saatler ve yeniden deneme
Algoritma süreçler arasında eşzamanlı bir saat olmadığını, ancak her süreçteki yerel saatin yaklaşık aynı hızda ilerlediğini ve hata payının kilit süresine göre küçük olduğunu varsayar. Belge bu nedenle karşılıklı dışlama kuralını daraltır: kural, kilidi tutan istemci işini geçerlilik süresi eksi saat kayması için birkaç milisaniye içinde bitirdiği sürece geçerlidir.
Kilidi alamayan istemci rastgele bir gecikmeden sonra yeniden denemelidir. Amaç, aynı kaynağı aynı anda isteyen istemcilerin eşzamanlılığını bozmaktır; aksi halde kimsenin kazanamadığı bir bölünmüş beyin (split brain) durumu oluşabilir. İstemci SET komutlarını N örneğe ne kadar aynı anda gönderirse bu pencere o kadar küçülür; belge bunun için çoklama (soketleri engellemesiz kipe alıp tüm komutları gönderip yanıtları sonra okumak) önerir. Çoğunluğu alamayan istemcinin kısmen aldığı kilitleri bir an önce bırakması da önemlidir, böylece diğerleri anahtarların süresinin dolmasını beklemek zorunda kalmaz.
Güvenlik ve canlılık gerekçeleri
Kilit çoğunlukta alındığında tüm örneklerdeki anahtarlar aynı süreye sahiptir, ama farklı anlarda yazılmıştır. İlk anahtarın en kötü T1'de, sonuncunun en kötü T2'de yazıldığını varsayarsak, ilk süresi dolacak anahtarın en az MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT kadar yaşayacağı kesindir. Çoğunluktaki anahtarlar ayaktayken başka bir istemci N/2+1 SET NX işlemini başaramaz, çünkü o anahtarlar zaten vardır. Çoğunluğu kilit süresine yakın veya ondan uzun sürede alan istemci de kilidi geçersiz sayıp bırakır.
Canlılık üç şeye dayanır: anahtarların süresinin dolmasıyla kilidin kendiliğinden bırakılması, istemcilerin işleri bitince veya kilidi alamayınca kilitleri genellikle kendilerinin bırakması ve yeniden denemeden önce çoğunluğu almak için gereken süreden belirgin biçimde uzun beklenmesi. Bedeli de belgede açıkça yazılıdır: ağ bölünmelerinde TTL kadar bir erişilemezlik cezası ödenir ve bölünmeler sürekli tekrarlanırsa sistem sınırsız süre erişilemez kalabilir.
Kalıcılık, çökme ve gecikmeli yeniden başlatma
Belge, kalıcılığın da güvenliği etkilediğini gösterir. Redis'i hiç kalıcılık olmadan çalıştırdığınızı düşünün: istemci kilidi beş örneğin üçünde alır, bunlardan biri yeniden başlar ve kilit anahtarı kaybolur. Artık aynı kaynak için kilitlenebilecek yine üç örnek vardır ve başka bir istemci kilidi alabilir. AOF kalıcılığı durumu iyileştirir; temiz bir SHUTDOWN ve yeniden başlatmada süreler sunucu kapalıyken de akmaya devam ettiği için sorun çıkmaz. Ama elektrik kesintisinde, varsayılan olan saniyede bir diske senkronizasyon ayarıyla anahtar yeniden başlatmadan sonra eksik olabilir. Her tür yeniden başlatmaya karşı güvenlik için belge her yazmada senkronizasyonu (fsync=always) anar ve bunun performans maliyeti olduğunu ekler.
Belgenin önerdiği daha hafif çözüm gecikmeli yeniden başlatmadır: çöken bir örnek, kullanılan en uzun TTL'den biraz daha uzun süre erişilemez tutulursa, çökme anında var olan tüm kilitlerin süresi dolmuş olur ve örnek döndüğünde hiçbir etkin kilide katılmaz. Bu yolla kalıcılık olmadan bile güvenlik sağlanabilir; bedeli, örneklerin çoğunluğu çökerse sistemin TTL süresince hiçbir kaynağı kilitleyememesidir. Redis kalıcılık seçeneklerinin kavramsal anlatımı Redis nedir yazısında yer alır.
Kilidi uzatmak
İş küçük adımlardan oluşuyorsa varsayılan olarak daha kısa kilit süreleri kullanıp bir uzatma mekanizması ekleyebilirsiniz. Belgeye göre istemci, geçerlilik süresi azaldığında tüm örneklere, anahtar varsa ve değeri hâlâ istemcinin rastgele değeriyse süreyi uzatan bir Lua betiği gönderir. Uzatma, ancak çoğunluk örnekte ve geçerlilik süresi içinde başarılı olursa sayılır; algoritma kilit almaya çok benzer. Belge ayrıca yeniden alma denemelerinin sınırlandırılması gerektiğini, aksi halde canlılık özelliklerinden birinin ihlal edileceğini vurgular.
Belge uzatma betiğinin metnini vermez, yalnızca ne yapması gerektiğini anlatır. Redis 8.4 ve sonrasında SET komutunun IFEQ seçeneği, anahtarın değerini ve süresini yalnızca mevcut değer verilen değere eşitse ayarlar ve anahtar yoksa oluşturmaz. Koşul seçenekleriyle süre seçenekleri ayrı gruplarda olduğu için ikisi birlikte yazılabilir; böylece aynı değeri yeniden yazıp süreyi tazeleyen tek komutluk bir uzatma elde edilir:
SET resource_name my_random_value IFEQ my_random_value PX 30000Komut sayfasına göre koşul sağlanmazsa yanıt nil, sağlanırsa OK olur. Redlock'ta bu komutu her örneğe gönderip OK sayısının çoğunluğa ulaşıp ulaşmadığına bakarsınız. Daha eski sürümlerde aynı kontrolü bırakma betiğindeki gibi GET karşılaştırması yapıp süreyi yenileyen bir Lua betiğiyle kurmanız gerekir.
Belgenin tutarlılık uyarıları

Sayfanın sonunda okurdan "Analysis of Redlock" bölümünü dikkatle incelemesi istenir: Martin Kleppmann'ın Redlock analizi ve antirez'in buna yanıtı çok ilgilidir. Tutarlılık ve doğruluk sizin için kritikse belge iki konuya dikkat çeker:
- Fencing token kullanın. Bu, özellikle uzun süren işler için önemlidir ve yalnızca Redis'e değil her dağıtık kilit sistemine uygulanır. Kilit ömrünü uzatmak da bir seçenektir, ama kilidi alan süreç hayatta olduğu sürece kilidin elinde kaldığını varsaymayın.
- Redis TTL için monoton saat kullanmaz. Duvar saatindeki bir kayma, kilidin birden fazla süreç tarafından alınmasına yol açabilir. Yöneticilerin sunucu saatini elle değiştirmesini engellemek ve NTP'yi doğru kurmak sorunu azaltır, ama ortadan kaldırmaz.
Fencing token fikri kısaca şudur: kilit her verildiğinde artan bir sayı da verilir ve korunan kaynak (örneğin veritabanı) daha küçük bir sayıyla gelen yazmaları reddeder. Böylece süresi dolmuş kilidiyle geç uyanan bir istemci, yeni sahibin yazdıklarının üzerine yazamaz. Belge bu kavramın uygulamasını anlatmaz; yalnızca gerekliliğini vurgular.
Hazır kütüphaneler
Algoritmayı her projede sıfırdan yazmak yerine belgedeki uygulama listesine bakabilirsiniz. Listede Go için Redsync, Java için Redisson, Node.js için node-redlock ve redlock-universal, Python için redlock-ng, Pottery ve Aioredlock, PHP için RedisMutex, Redlock-php ve cheprasov/php-redis-lock, .NET için RedLock.net ve Redlock-cs, Rust için Rslock gibi uygulamalar yer alır. Bunların bir kısmının kilit uzatma desteği olduğu belgede belirtilir. Kütüphane seçerken rastgele değer üretimini, bırakma işleminin atomik olup olmadığını ve uzatmanın çoğunluk kuralına uyup uymadığını kontrol edin.
Sık yapılan hatalar
- Süresiz kilit:
PXveyaEXolmadan alınan kilit, istemci çökerse hiç bırakılmaz ve canlılık özelliği kaybolur. - Sabit değer: Tüm istemcilerin aynı değeri yazması, kimin kilidi tuttuğunu ayırt etmeyi imkansız kılar.
- Düz DEL ile bırakma: Süresi dolup başkasına geçmiş kilidi silebilir.
- Ana-kopya düzenine güvenmek: Eşzamansız replikasyon yüzünden yük devri sırasında iki istemci aynı kilidi alabilir.
- Geçerlilik süresini hesaba katmamak: Kilidi aldıktan sonra kalan süre, başlangıç süresinden alım süresi düşülerek bulunur; işi bu sürede bitirmeyen istemci artık korunmaz.
- Sınırsız uzatma: Uzatma denemeleri sınırlanmazsa diğer istemciler kilidi hiç alamayabilir.
Kilit yerine Redis'in iyimser kilit mekanizması (WATCH) sizin için yeterli olabilir mi diye de düşünün: aynı anahtar üzerinde oku-değiştir-yaz yapıyorsanız MULTI, EXEC ve WATCH yazısındaki yaklaşım ayrı bir kilit anahtarına gerek bırakmaz.
Sık Sorulan Sorular
Tek Redis örneği ile kilit kullanmak yanlış mı?
Hayır. Belge tek örnek yöntemini, ara sıra bir yarış durumunun kabul edilebildiği uygulamalar için uygulanabilir bir çözüm olarak tanımlar. Örnek çöktüğünde kilitler kaybolabilir veya yük devrinde iki istemci aynı kilidi alabilir; bu sizin için kabul edilemezse Redlock'a ve belgenin uyarılarına bakın.
DELEX hangi sürümde var?
Belgeye göre DELEX Redis 8.4 ile geldi; SET komutunun IFEQ gibi karşılaştırmalı seçenekleri de 8.4 sürüm notlarında yer alır. Daha eski sunucularda bırakma için Lua betiğini kullanın.
Redlock için kaç Redis sunucusu gerekir?
Belgenin örneği, büyük ölçüde bağımsız arızalanmaları için farklı makinelerde çalışan beş bağımsız ana sunucu kullanır ve kilidin en az üçünde alınmasını şart koşar. Sunucular arasında replikasyon olmamalıdır.
Kilit süresini ne kadar seçmeliyim?
İşinizin en kötü durumdaki süresinden uzun, ama çöken bir istemcinin kilidi gereğinden uzun süre tutmayacağı kadar kısa. Ağ bölünmelerinde bu süre kadar erişilemezlik cezası ödendiğini ve gecikmeli yeniden başlatma yaklaşımında bekleme süresinin de en uzun TTL'ye bağlı olduğunu unutmayın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Redis öğrenme rehberi · String komutları: SET, GET, INCR · Redis Lua script · MULTI, EXEC ve WATCH · EXPIRE ve TTL · Redis iş kuyruğu