Redis Cluster anahtarları 16384 hash slot'a böler ve her birincil (primary) düğüm bu slotların bir kısmına hizmet eder. Bir anahtarın hangi slota düştüğünü CLUSTER KEYSLOT söyler. Birden çok anahtar kullanan komutlar (MGET, MSET, SINTER, transaction'lar ve Lua betikleri) yalnızca tüm anahtarlar aynı slottaysa çalışır; değilse CROSSSLOT Keys in request don't hash to the same slot hatası gelir. Çözüm, anahtar adında süslü parantezle yazılan hash tag'dir: user:{42}:profile ve user:{42}:cart her zaman aynı slottadır.

Bu yazı, bir cluster'a bağlanan geliştiricinin görmesi gerekenleri anlatıyor: slot hesabı, hash tag kullanımı, hangi komutların slot kuralına tabi olduğu, MOVED, ASK, TRYAGAIN ve CLUSTERDOWN hatalarının anlamı ve CLUSTER INFO, CLUSTER NODES, CLUSTER SHARDS ile durumu okumak. Cluster'ın kurulumu ve yapılandırması bu yazıda yok; dayandığımız belgeler bu konuyu kapsamıyor. Cluster kavramına kısa bir giriş Redis nedir yazısında, serinin geri kalanı Redis öğrenme rehberi sayfasında.

Hash slot: anahtar hangi düğümde?

Cluster'da her anahtar, adından hesaplanan bir slot numarasına (0 ile 16383 arası) eşlenir; slotların her biri o an tek bir birincil düğüme aittir. İstemci bir komut gönderdiğinde, komutun anahtarı hangi slottaysa o slotun sahibi olan düğüme gitmesi gerekir. CLUSTER KEYSLOT bu hesabı sunucuya yaptırır ve slot numarasını tamsayı olarak döndürür. Komut sayfası iki kullanım alanı sayar: istemci kütüphanelerinin kendi hash hesaplarını rastgele anahtarlarla Redis'inkiyle karşılaştırması ve bir insanın bir anahtarın hangi slota, dolayısıyla hangi düğüme düştüğünü kontrol etmesi. Belgedeki örnek:

text
> CLUSTER KEYSLOT somekey
(integer) 11058
> CLUSTER KEYSLOT foo{hash_tag}
(integer) 2515
> CLUSTER KEYSLOT bar{hash_tag}
(integer) 2515

İkinci ve üçüncü satırda anahtar adları farklı olduğu halde slot aynıdır, çünkü iki ad da geçerli bir hash tag içeriyor ve Redis yalnızca { ile } arasındaki hash_tag kısmını hash'liyor. Komut, hash tag desteği dahil cluster'ın tam hash algoritmasını kullanır; yani burada gördüğünüz sonuç, cluster'ın anahtarı gerçekten yerleştireceği slottur.

Hash tag ile ilişkili anahtarları bir arada tutmak

Hash tag, birlikte kullanılacak anahtarları aynı slota zorlamanın yoludur. Bir kullanıcıya ait profil, sepet ve oturum anahtarlarını tek bir MSET ya da tek bir transaction içinde işlemek istiyorsanız, adlarında ortak olan kısmı süslü paranteze alırsınız. CROSSSLOT hata sayfasındaki örnek bunu adım adım gösterir:

text
> CLUSTER KEYSLOT user:1
(integer) 10778
> CLUSTER KEYSLOT user:2
(integer) 6777
> MGET user:1 user:2
(error) CROSSSLOT Keys in request don't hash to the same slot
text
> CLUSTER KEYSLOT user:{42}:profile
(integer) 8000
> CLUSTER KEYSLOT user:{42}:cart
(integer) 8000
> MSET user:{42}:profile "..." user:{42}:cart "..."
OK

İlk oturumda user:1 ve user:2 farklı slotlara düştüğü için tek bir MGET reddediliyor. İkinci oturumda ise iki anahtarın da hash tag'i 42 olduğu için ikisi de 8000 numaralı slotta ve MSET başarıyla çalışıyor. Anahtar adlandırma düzeninizi tasarlarken bu kuralı baştan hesaba katmak, sonradan tüm anahtarları yeniden adlandırmaktan çok daha kolaydır; adlandırma önerileri için anahtar isimlendirme ve keyspace yazısına bakın.

Hangi komutlar slot kuralına tabi?

Hangi komutlar slot kuralına tabi? DBSIZE, KEYS, SCAN, FLUSHALL, FLUSHDB, EXISTS, MGET, DEL, MSET, TOUCH, UNLINK, SINTER, SUNION, SDIFF, SINTERCARD, ZINTER, ZUNION, ZDIFF...

Multi-key işlemler sayfası, aynı komutun tek sunuculu Redis'te ve cluster'da nasıl farklı davrandığını tablolarla verir. Tek sunucuda (cluster kapalı) tüm çok anahtarlı komutlar herhangi bir anahtar kombinasyonuyla çalışır. Redis Open Source cluster'ında ise davranış üç gruba ayrılır. Aşağıdaki tablo yalnızca Redis Open Source cluster satırlarını özetler; sayfadaki Redis Software ve Active-Active satırları bu yazının kapsamı dışında.

KomutlarRedis Open Source cluster davranışı
DBSIZE, KEYS, SCANYalnızca bağlı olunan düğümdeki (shard) anahtarlara bakar
FLUSHALL, FLUSHDBYalnızca bağlı olunan düğümü etkiler
EXISTS, MGET, DEL, MSET, TOUCH, UNLINKTek slot: tüm anahtarlar aynı slotta olmalı
SINTER, SUNION, SDIFF, SINTERCARD, ZINTER, ZUNION, ZDIFF, PFCOUNT, WATCH, XREAD, XREADGROUPTek slot
RENAME, COPY, LMOVE, BLPOP, SMOVE, SUNIONSTORE, ZUNIONSTORE, PFMERGE, BITOP, MSETNX ve benzerleriTek slot
JSON.MGETCROSSSLOT vermez; anahtarlar farklı slotlardaysa yalnızca o düğümdeki slotların kısmi sonucunu döndürür
TS.MGET, TS.MRANGE, TS.MREVRANGE, TS.QUERYINDEXTüm düğümlerde çalışır ama transaction içinde kullanılamaz
Pipeline, MULTI/EXEC, EVAL ve EVALSHA içindeki anahtarlarTek slot

Tablodan çıkan iki pratik sonuç var. Birincisi, cluster'da KEYS, SCAN ve DBSIZE tüm veriyi değil yalnızca bağlandığınız düğümü görür; tüm anahtar uzayını taramak için her birincil düğümde ayrı ayrı tarama yapmanız gerekir. Taramanın kendisi SCAN ve KEYS farkı yazısında anlatılıyor. İkincisi, transaction'lar ve Lua betikleri de tek slot kuralına tabidir: MULTI/EXEC ya da EVAL kullanan kodunuzu cluster'a taşırken betiğin dokunduğu bütün anahtarların aynı hash tag'i taşıdığından emin olun. Sayfa ayrıca TS.CREATERULE ve TS.DELETERULE için ilginç bir tuzağa dikkat çeker: iki anahtar aynı slotta değilse CROSSSLOT yerine ERR TSDB: the key does not exist hatası alırsınız.

Hash tag kullanamadığınız durumda hata sayfası ikinci bir yol gösterir: komutu bölmek. Her anahtar için ya da aynı slottaki her anahtar grubu için ayrı komut gönderirsiniz. Bazı cluster istemcileri bunu sizin yerinize yapar; örneğin redis-py'nin RedisCluster sınıfında mget_nonatomic() ve mset_nonatomic() yöntemleri vardır. Adlarının da söylediği gibi bu durumda anahtarlar artık tek adımda okunmaz ya da yazılmaz; arada başka bir istemci değişiklik yapabilir.

MOVED ve ASK: yönlendirmeler

Bir komutu, anahtarın slotuna hizmet etmeyen bir düğüme gönderirseniz düğüm komutu çalıştırmaz, size doğru adresi söyler. MOVED hatası slot numarasını ve o slotu sunan düğümün adresini içerir. Cluster destekli bir istemci bu hatayı okur, komutu doğru düğüme yeniden gönderir ve kendi cluster haritasını günceller. Belgedeki örnek:

text
> GET foo
(error) MOVED 12182 127.0.0.1:6463

Bu hatayı uygulamanızda görüyorsanız neredeyse her zaman sebep, cluster'ı desteklemeyen bir istemci kullanmanızdır. Hata sayfası iki çözüm verir: redis-py'de Redis yerine RedisCluster kullanmak ve redis-cli'de -c seçeneğini eklemek. redis-cli yardım çıktısı bu seçeneği, -ASK ve -MOVED yönlendirmelerini izleyen cluster modu olarak tanımlar:

bash
redis-cli -c -h 127.0.0.1 -p 30001

ASK ise bir slot bir düğümden diğerine taşınırken, örneğin cluster yeniden dağıtılırken görülür. İstediğiniz anahtar eski düğümde artık yoksa yeni düğüme geçmiş olabilir; hata, slot numarasını ve yeni düğümün adresini verir:

text
> GET user:{42}:cart
(error) ASK 8000 127.0.0.1:6461

MOVED ile ASK arasındaki fark ince ama önemlidir. Yönlendirmeleri kendiniz işliyorsanız, ASK aldığınızda önce hedef düğüme ASKING komutunu, ardından asıl komutu gönderirsiniz. ASKING olmadan yeni düğüm size MOVED ile cevap verip sizi eski düğüme geri yollar. Ayrıca MOVED'dan farklı olarak ASK aldığınızda cluster haritanızı güncellemezsiniz: taşıma bitene kadar slot hâlâ eski düğüme aittir ve sonraki komutları önce oraya göndermeniz gerekir. ASKING sayfası, transaction sırasında ASK gelirse transaction'ın tamamından önce hedef düğüme tek bir ASKING göndermenin yettiğini de belirtir. Neyse ki cluster istemcileri bunların hepsini kendiliğinden yapar.

TRYAGAIN ve CLUSTERDOWN

TRYAGAIN Multiple keys request during rehashing of slot hatası, çok anahtarlı bir komut, anahtarlarının slotu taşınırken çalıştırıldığında gelir. Anahtarların bir kısmı hâlâ eski düğümde, bir kısmı yeni düğümdedir ve iki düğüm de komutu tek başına çalıştıramaz. Hata geçicidir; slotun taşınması bittiğinde kaybolur. Belgedeki örnekte eski düğümde user:{42}:profile duruyor, user:{42}:cart ise çoktan yeni düğüme geçmiş:

text
> MGET user:{42}:profile user:{42}:cart
(error) TRYAGAIN Multiple keys request during rehashing of slot

Yapılacak şey kısa bir bekleme ve yeniden denemedir; cluster istemcileri genellikle bunu kendileri yapar. Hata sayfasına göre redis-py'nin RedisCluster sınıfı komutu birkaç kez dener ve slot hâlâ taşınıyorsa ClusterError: TTL exhausted. hatası verir. Hata kaybolmuyorsa slot bir taşımanın ortasında takılmış olabilir; sayfa bunu redis-cli --cluster check <host>:<port> ile kontrol etmeyi önerir, çünkü bu komut hâlâ açık (taşınmakta olan) slotları listeler. Benzer bir mesaj olan TRYAGAIN Slot is being trimmed ise bir düğüm bir slotun tüm anahtarlarını silerken gelen yazmalara verilir ve o da geçicidir.

CLUSTERDOWN daha ciddi bir durumdur ve iki biçimi vardır. CLUSTERDOWN Hash slot not served, anahtarın slotuna hiçbir düğümün hizmet etmediğini, örneğin kurulumu bitmemiş bir cluster'da bazı slotların hiç atanmadığını söyler. CLUSTERDOWN The cluster is down ise cluster'ın hata durumunda olduğunu söyler: varsayılan olarak, herhangi bir slotun çalışan bir düğümü kalmadığında cluster tüm komutları reddeder. Belgedeki örnekte replikası olmayan bir birincil düşünce diğer düğümler kendi anahtarları için bile hizmet vermeyi bırakır:

text
> GET foo
(error) CLUSTERDOWN The cluster is down

Hata sayfasının önerdiği teşhis sırası şöyledir: önce CLUSTER INFO ile cluster_state değerine bakın (fail cluster'ın düştüğünü gösterir) ve cluster_slots_assigned ile 16384 slottan kaçının bir düğümü olduğunu görün. Sonra CLUSTER NODES ile bayraklarında fail bulunan düğümleri bulun. Düşen düğümü geri getirin ya da slotlarını çalışan düğümlere taşıyın. Bir birincil düştüğünde cluster'ın çalışmaya devam etmesi için her birincile en az bir replika vermek gerekir; replika düşen birincilin yerini alır. Davranışı değiştiren iki ayar da vardır: cluster-require-full-coverage no düğümlerin hâlâ çalışan düğümü olan slotlara hizmet etmeyi sürdürmesini, cluster-allow-reads-when-down yes ise cluster hata durumundayken okumalara izin verilmesini sağlar; bu durumda yazmalar CLUSTERDOWN The cluster is down and only accepts read commands hatası alır.

Cluster durumunu okumak

Cluster durumunu okumak: CLUSTER INFO, CLUSTER NODES ve CLUSTER SHARDS, Slot başına komutlar

CLUSTER INFO

CLUSTER INFO, INFO komutuna benzer biçimde cluster'ın temel parametrelerini alan:değer satırlarıyla verir. Belgedeki örnek, her zaman bulunan alanları gösterir:

text
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:2
cluster_stats_messages_sent:1483972
cluster_stats_messages_received:1483968
total_cluster_links_buffer_limit_exceeded:0

cluster_state düğüm sorgu alabiliyorsa ok, en az bir slot atanmamışsa, hata durumundaki bir düğüme aitse ya da düğüm birincillerin çoğunluğuna ulaşamıyorsa fail olur. cluster_slots_assigned bir düğüme atanmış slot sayısıdır ve düzgün çalışma için 16384 olmalıdır. cluster_slots_pfail, PFAIL durumundaki düğümlere ait slotları sayar; PFAIL yalnızca o an düğümle konuşulamadığını söyler ve hata algılama süreci onu FAIL'e yükseltmedikçe bu slotlar çalışmaya devam eder. cluster_slots_fail sıfırdan büyükse, cluster-require-full-coverage ayarı no değilse düğüm sorgulara hizmet edemez. cluster_size en az bir slota hizmet eden birincil sayısı, cluster_known_nodes el sıkışma aşamasındakiler dahil bilinen düğüm sayısıdır. Örnekte 6 düğümlü, 3 birincilli ve sağlıklı bir cluster görüyoruz. Redis 8.4 ile gelen atomik slot taşıma için ek alanlar da (cluster_slot_migration_active_tasks gibi) bu çıktıda görünebilir.

CLUSTER NODES ve CLUSTER SHARDS

CLUSTER NODES, bağlandığınız düğümün cluster'ı nasıl gördüğünü her düğüm için bir satırla verir. Satırların alanları sırasıyla düğüm kimliği (40 karakterlik, değişmeyen bir dize), ip:port@cport adresi ve isteğe bağlı ana makine adı, bayraklar, replikaysa birincilin kimliği, ping ve pong zamanları, yapılandırma dönemi (config epoch), bağlantı durumu ve hizmet edilen slot aralıklarıdır. Belgedeki örnekten iki satır:

text
67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002@31002,hostname2 master - 0 1426238316232 2 connected 5461-10922
6ec23923021cf3ffec47632106199cb7f496ce01 127.0.0.1:30005@31005,hostname5 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 0 1426238316232 5 connected

İlk satır 5461-10922 slotlarına hizmet eden bir birincildir; ikinci satır, dördüncü alanında birincilin kimliğini taşıyan onun replikasıdır. Bayraklar arasında myself bağlandığınız düğümü, fail? PFAIL durumunu, fail birden çok düğümün ulaşamadığı FAIL durumunu, handshake henüz güvenilmeyen bir düğümü, noaddr adresi bilinmeyen düğümü gösterir. Taşınan slotlar yalnızca myself satırında [93-<-...] (içeri alınıyor) ya da [77->-...] (dışarı veriliyor) biçiminde görünür. Komut sayfası, slot ve adres eşlemesini öğrenmek isteyen istemcilerin bu komut yerine CLUSTER SHARDS kullanmasını, CLUSTER NODES'un ise yönetim, hata ayıklama ve yapılandırma incelemesi için kullanılmasını önerir.

CLUSTER SHARDS, eski CLUSTER SLOTS komutunun yerini alan, daha verimli ve genişletilebilir bir gösterimdir. Her shard için slots (başlangıç ve bitiş çiftleri hâlinde slot aralıkları) ve nodes (düğümlerin id, endpoint, ip, port, role, replication-offset ve health alanları) döner. health değeri online, failed ya da loading olabilir ve hangi düğüme trafik gönderileceğine karar vermek için kullanılır. Sayfaya göre istemciler açılışta bu komutu çağırıp slot haritasını çıkarmalı ve bir MOVED aldıklarında haritayı bununla tazelemelidir.

Slot başına komutlar

Tek bir slotu incelemek için iki komut vardır. CLUSTER COUNTKEYSINSLOT bir slottaki anahtar sayısını verir, CLUSTER GETKEYSINSLOT ise bir slottaki anahtar adlarını count ile sınırlayarak döndürür. İkisi de yalnızca bağlandığınız düğümün yerel verisine bakar; slotun sahibi olmayan bir düğümde sayım her zaman sıfır çıkar. Belgedeki örnekler:

text
> CLUSTER COUNTKEYSINSLOT 7000
(integer) 50341
> CLUSTER GETKEYSINSLOT 7000 3
1) "key_39015"
2) "key_89793"
3) "key_92937"

Redis 8.2 ile gelen CLUSTER SLOT-STATS ise geçerli shard'daki slotlar için kullanım istatistikleri döndürür: anahtar sayısı (KEY-COUNT), CPU süresi, bellek ve ağ trafiği. SLOTSRANGE ile bir aralık seçebilir ya da ORDERBY ile bir ölçüye göre sıralayıp LIMIT ile ilk birkaç slotu alabilirsiniz; böylece sıcak slotları, yani yanlış seçilmiş bir hash tag'in yarattığı yığılmaları bulursunuz. KEY-COUNT dışındaki ölçüler cluster-slot-stats-enabled ayarının yes olmasını gerektirir.

bash
redis-cli -c -p 30001 CLUSTER SLOT-STATS ORDERBY KEY-COUNT LIMIT 10 DESC
redis-cli -c -p 30001 CLUSTER SLOT-STATS SLOTSRANGE 0 100

Replikalardan okuma: READONLY

Varsayılan olarak cluster'daki bir replika, kendisine gelen komutları slotun sahibi olan birincile yönlendirir. Okuma yükünü replikalara dağıtmak istiyorsanız bağlantıda READONLY komutunu çalıştırırsınız. Bu komut replikaya, istemcinin olası eski veriyi okumayı kabul ettiğini ve yazma yapmayacağını söyler. Bu moddaki bir bağlantı yalnızca komut, replikanın birincilinin sunmadığı slotlara dokunuyorsa yönlendirilir. Okuma tutarlılığı sizin için kritikse bu modu kullanmayın.

Uygulamadan bağlanmak: redis-py örneği

Cluster istemcileri slot haritasını kendileri tutar, MOVED ve ASK yönlendirmelerini izler ve TRYAGAIN durumunda yeniden dener. redis-py bağlantı sayfasındaki örnek, RedisCluster sınıfıyla tek bir düğüm adresi vererek bağlanmayı gösterir; çıktı yorumları sayfadan alınmıştır:

python
from redis.cluster import RedisCluster

rc = RedisCluster(host='localhost', port=16379)

print(rc.get_nodes())
# [[host=127.0.0.1,port=16379,name=127.0.0.1:16379,server_type=primary,redis_connection=Redis<ConnectionPool<Connection<host=127.0.0.1,port=16379,db=0>>>], ...

rc.set('foo', 'bar')
# True

rc.get('foo')
# b'bar'

Uygulama kodunuz tek sunucuya bağlanırken Redis, cluster'a bağlanırken RedisCluster kullanır; geri kalan komut çağrıları büyük ölçüde aynıdır. Fark, çok anahtarlı işlemlerde ortaya çıkar ve yukarıdaki slot kuralları burada da geçerlidir. redis-py'nin genel kullanımı redis-py kullanımı yazısında, diğer diller node-redis, go-redis ve Jedis ve Lettuce yazılarında.

Sık yapılan hatalar

Sık yapılan hatalar: Tek sunucu için yazılmış kodu doğrudan cluster'a taşımak, Herkese aynı hash tag'i vermek, Cluster'da KEYS ya da DBSIZE sonucunu tüm veri sanmak, ASK aldıktan...
  • Tek sunucu için yazılmış kodu doğrudan cluster'a taşımak: MGET, transaction ve Lua betikleri farklı slotlardaki anahtarlarla CROSSSLOT verir.
  • Herkese aynı hash tag'i vermek: Tüm veri tek düğüme yığılır, cluster'ın ölçekleme faydası kaybolur.
  • Cluster'da KEYS ya da DBSIZE sonucunu tüm veri sanmak: Bu komutlar yalnızca bağlı olunan düğüme bakar.
  • ASK aldıktan sonra haritayı güncellemek: Taşıma bitene kadar slot eski düğüme aittir; yalnızca MOVED haritayı değiştirir.
  • redis-cli'yi -c olmadan kullanıp MOVED hatasına şaşırmak: Cluster modunu açan seçenek -c'dir.
  • Replikasız birincil bırakmak: Bir birincil düştüğünde varsayılan ayarlarla cluster'ın tamamı CLUSTERDOWN verir.

Sık Sorulan Sorular

Neden 16384 slot var, bu sayıyı değiştirebilir miyim?

Bu yazının dayandığı sayfalar 16384 sayısını cluster'ın sabit bir özelliği olarak verir ve değiştirmenin bir yolundan söz etmez. Slotların düğümlere dağılımını ise değiştirebilirsiniz; bu yeniden dağıtma (resharding) işlemi cluster yönetimi konusudur ve dayandığımız belgelerin kapsamı dışındadır.

Hash tag'te süslü parantez boşsa ya da birden çok parantez varsa ne olur?

CLUSTER KEYSLOT sayfası yalnızca "geçerli bir hash tag" içeren anahtarlarda parantez arasının hash'lendiğini söyler; geçerliliğin kuralları cluster belirtiminde anlatılır ve o sayfa yerel belgelerimizde yok. Emin olmadığınız bir anahtar adı için en güvenli yol, CLUSTER KEYSLOT ile slotu sunucuya sormaktır.

Pub/Sub cluster'da nasıl çalışır?

CLUSTER INFO sayfası, normal yayınların ve shard yayınlarının cluster veri yolu üzerinden ayrı mesaj türleriyle taşındığını gösterir. Shard'lara bağlı SSUBSCRIBE ve SPUBLISH komutları Redis Pub/Sub yazısında anlatılıyor.

Hata mesajlarının tam listesine nereden bakarım?

Cluster hataları dahil en yaygın hatalar için Redis hata mesajları ve OOM, MISCONF ve READONLY yazılarımıza, Redis'in resmî hata dizinine de notlardaki bağlantılardan ulaşabilirsiniz.

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 · Anahtar isimlendirme · MULTI/EXEC · Lua script · Redis hata mesajları · Redis INFO