Redis set, birbirinden farklı (tekil) string üyelerden oluşan sırasız bir koleksiyondur. SADD ile üye eklersiniz ve aynı üyeyi ikinci kez eklemek hiçbir şeyi değiştirmez; SISMEMBER ile bir üyenin kümede olup olmadığını sabit sürede sorarsınız; SINTER, SUNION ve SDIFF ile birden çok küme arasında kesişim, birleşim ve fark hesaplarsınız. Tekrarları kendiniz ayıklamak zorunda kalmadan "benzersiz olanları tut" diyebildiğiniz her yerde set doğru tiptir.

Bu yazıda set komutlarını resmî Redis belgelerindeki örneklerle ele alıyoruz: ekleme ve üyelik testi, küme işlemleri ve sonuçlarını saklayan *STORE komutları, yalnızca sayı döndüren SINTERCARD ile Redis 8.10 ile gelen SUNIONCARD ve SDIFFCARD, rastgele üye seçimi, performans ve alternatifler. Redis'in genel tanıtımı için Redis nedir, nasıl kullanılır, serinin tamamı için Redis öğrenme rehberi sayfasına bakabilirsiniz.

Set ne işe yarar?

Set ne işe yarar?

Belgeler set için üç tipik kullanım sayar. Birincisi tekil öğeleri izlemektir; örneğin bir blog yazısına erişen bütün farklı IP adreslerini tutmak. İkincisi ilişkileri temsil etmektir; örneğin belirli bir role sahip bütün kullanıcıların kümesi. Üçüncüsü kesişim, birleşim ve fark gibi küme işlemleridir; örneğin "hem şu gruba hem bu gruba üye olanlar" sorusu tek komutla yanıtlanır.

Set'in iki temel özelliği vardır. Üyeler tekildir, bu yüzden aynı değeri on kez eklemek bir kez eklemekle aynı sonucu verir. Üyeler sırasızdır, yani Redis üyeleri her çağrıda farklı bir sırayla döndürmekte serbesttir. Bir set en fazla 2^32 - 1 (4.294.967.295) üye tutabilir. Üyeleri bir puana göre sıralamanız gerekiyorsa sorted set tipine geçmeniz gerekir.

SADD ve SMEMBERS ile ilk küme

SADD bir veya daha fazla üye ekler ve gerçekten eklenen üye sayısını döndürür; kümede zaten olan üyeler sayılmaz. Anahtar yoksa yeni bir set oluşturulur, anahtar başka bir tip tutuyorsa hata döner. Belgelerdeki örnekte Fransa ve ABD'de yarışan bisikletler iki ayrı kümede tutulur:

text
> SADD bikes:racing:france bike:1
(integer) 1
> SADD bikes:racing:france bike:1
(integer) 0
> SADD bikes:racing:france bike:2 bike:3
(integer) 2
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2

İkinci komutun 0 döndürmesi önemlidir: aynı üye tekrar eklenmeye çalışıldığında Redis hata vermez, yalnızca hiçbir şey eklemediğini söyler. Bu dönüş değerini "bu öğeyi daha önce gördüm mü?" sorusunun cevabı olarak kullanabilirsiniz; tek komutla hem kontrol hem ekleme yapmış olursunuz.

SMEMBERS kümedeki bütün üyeleri döndürür. Belgeler sıranın garanti edilmediğini özellikle vurgular:

text
> DEL bikes:racing:france
(integer) 1
> SADD bikes:racing:france bike:1 bike:2 bike:3
(integer) 3
> SMEMBERS bikes:racing:france
1) "bike:3"
2) "bike:1"
3) "bike:2"

Üyelik testi: SISMEMBER, SMISMEMBER ve SCARD

SISMEMBER tek bir üyenin kümede olup olmadığını 1 ya da 0 ile söyler. Anahtar yoksa da 0 döner. Belgeler bu işlemin O(1) olduğunu ve bir listede arama yapmaktan çok daha hızlı olduğunu belirtir; listede arama eleman sayısıyla orantılıdır.

text
> SISMEMBER bikes:racing:usa bike:1
(integer) 1
> SISMEMBER bikes:racing:usa bike:2
(integer) 0

Birkaç üyeyi tek seferde kontrol etmek için SMISMEMBER kullanın. Yanıt, istediğiniz sırayla her üye için 1 ya da 0 içeren bir dizidir; böylece sunucuya tek bir gidiş dönüşle birden çok soru sorarsınız.

text
> SMISMEMBER bikes:racing:france bike:2 bike:3 bike:4
1) (integer) 1
2) (integer) 1
3) (integer) 0

SCARD kümenin eleman sayısını (kardinalitesini) verir; anahtar yoksa 0 döner. Önceki örnekteki Fransa kümesi için SCARD bikes:racing:france üç döndürür.

Küme işlemleri: SINTER, SUNION ve SDIFF

Küme işlemleri: SINTER, SUNION ve SDIFF

Set'i diğer tiplerden ayıran asıl güç küme işlemleridir. SINTER bütün kümelerde ortak olan üyeleri, SUNION en az bir kümede bulunan bütün üyeleri, SDIFF ise ilk kümede olup sonraki kümelerin hiçbirinde olmayan üyeleri döndürür. Belgelerdeki üç yarışlı örnek bunların hepsini gösterir:

text
> DEL bikes:racing:france bikes:racing:usa bikes:racing:italy
(integer) 2
> SADD bikes:racing:france bike:1 bike:2 bike:3
(integer) 3
> SADD bikes:racing:usa bike:1 bike:4
(integer) 2
> SADD bikes:racing:italy bike:1 bike:2 bike:3 bike:4
(integer) 4
> SINTER bikes:racing:france bikes:racing:usa bikes:racing:italy
1) "bike:1"
> SUNION bikes:racing:france bikes:racing:usa bikes:racing:italy
1) "bike:2"
2) "bike:1"
3) "bike:4"
4) "bike:3"
> SDIFF bikes:racing:france bikes:racing:usa bikes:racing:italy
(empty array)
> SDIFF bikes:racing:france bikes:racing:usa
1) "bike:3"
2) "bike:2"
> SDIFF bikes:racing:usa bikes:racing:france
1) "bike:4"

Son iki komut kritik bir kuralı gösterir: fark işlemi değişmeli değildir. "Fransa'da olup ABD'de olmayanlar" ile "ABD'de olup Fransa'da olmayanlar" farklı sonuç verir; SDIFF'te argüman sırası sonucu belirler. Üç kümeli fark ise boş dizi döner, çünkü Fransa'daki bütün bisikletler İtalya kümesinde de vardır.

Var olmayan anahtarlar boş küme kabul edilir. Bunun sonucu, kesişimde tek bir boş ya da yanlış yazılmış anahtar bütün sonucu boşaltır; birleşimde ise sessizce yok sayılır. Bir küme işlemi beklenmedik şekilde boş dönüyorsa önce anahtar adlarını EXISTS ile kontrol edin.

Sonucu saklamak: SINTERSTORE, SUNIONSTORE, SDIFFSTORE

Her küme işleminin sonucu bir anahtara yazan STORE sürümü vardır. Sonuç istemciye gönderilmez, belirttiğiniz hedef anahtara yazılır ve komut sonuç kümesinin eleman sayısını döndürür. Hedef anahtar zaten varsa üzerine yazılır.

text
> SADD key1 "a"
(integer) 1
> SADD key1 "b"
(integer) 1
> SADD key1 "c"
(integer) 1
> SADD key2 "c"
(integer) 1
> SADD key2 "d"
(integer) 1
> SADD key2 "e"
(integer) 1
> SINTERSTORE key key1 key2
(integer) 1
> SMEMBERS key
1) "c"

Aynı key1 ve key2 kümeleriyle belgelerdeki diğer iki örnek şunu gösterir: SUNIONSTORE key key1 key2 5 döndürür ve hedefte a, b, c, d, e üyeleri bulunur; SDIFFSTORE key key1 key2 ise 2 döndürür ve hedefte yalnızca a ile b kalır. Hedef anahtar olarak kaynaklardan birini verirseniz sonuç o kümenin üzerine yazılır; bu bazen istenen bir davranıştır (örneğin bir kümeyi diğerinden çıkarıp yerinde güncellemek), bazen de veri kaybına yol açan bir hatadır.

Belgeler bu komutları, karmaşık küme hesaplarını önceden hesaplayıp saklamak için önerir. Örneğin her gece "bu hafta hem giriş yapmış hem satın alma yapmış kullanıcılar" kümesini hesaplayıp bir anahtara yazar, gün boyunca yalnızca o anahtarı okursunuz. Saklanan sonucun eskimesini istemiyorsanız hedef anahtara bir süre verebilirsiniz; ayrıntı için EXPIRE ve TTL yazısına bakın.

Yalnızca sayı: SINTERCARD, SUNIONCARD ve SDIFFCARD

Çoğu zaman kesişimin üyelerini değil yalnızca kaç üye olduğunu bilmek istersiniz. SINTERCARD sonuç kümesini döndürmeden yalnızca eleman sayısını verir. İlk argüman anahtar sayısıdır (numkeys). İsteğe bağlı LIMIT, sayım bu değere ulaştığında hesabı durdurur ve sonuç olarak limiti döndürür; varsayılan 0 sınırsız demektir. Belgeler bunun, limitin gerçek kesişimden küçük olduğu sorgularda ciddi hız kazandırdığını belirtir.

text
> SADD key1 "a"
(integer) 1
> SADD key1 "b"
(integer) 1
> SADD key1 "c"
(integer) 1
> SADD key1 "d"
(integer) 1
> SADD key2 "c"
(integer) 1
> SADD key2 "d"
(integer) 1
> SADD key2 "e"
(integer) 1
> SINTER key1 key2
1) "c"
2) "d"
> SINTERCARD 2 key1 key2
(integer) 2
> SINTERCARD 2 key1 key2 LIMIT 1
(integer) 1

LIMIT, "en az N ortak üye var mı?" sorusu için idealdir. Örneğin iki kullanıcının en az üç ortak ilgi alanı olup olmadığını öğrenmek için LIMIT 3 verirsiniz; sayım üçe ulaştığı anda durur.

Redis 8.10 ile aynı mantık birleşim ve fark için de geldi; bu iki komut en az Redis 8.10 gerektirir. SUNIONCARD birleşimdeki farklı üye sayısını, SDIFFCARD ilk kümede olup diğerlerinde olmayan üye sayısını döndürür ve ikisi de LIMIT kabul eder. SUNIONCARD ayrıca APPROX seçeneğine sahiptir: bu durumda birleşimi hiç oluşturmadan, içeride HyperLogLog kullanarak yaklaşık %0,81 standart hatayla tahmin yapar. Belgeler bunu, kesin sayımın hem zaman hem bellek açısından pahalı olduğu çok büyük birleşimler için önerir.

text
> SUNIONCARD 2 key1 key2
(integer) 5
> SUNIONCARD 2 key1 key2 LIMIT 3
(integer) 3
> SDIFFCARD 2 key1 key2
(integer) 2
> SDIFFCARD 2 key1 key2 LIMIT 1
(integer) 1

Bu çıktılar belgelerdeki örneklerden alınmıştır; her iki sayfada key1 kümesi a, b, c ve key2 kümesi c, d, e üyelerini tutar. Yaklaşık sayım mantığı için HyperLogLog yazısına bakabilirsiniz.

Silme ve rastgele seçim: SREM, SPOP, SRANDMEMBER, SMOVE

SREM belirttiğiniz üyeleri siler ve gerçekten silinen sayısını döndürür. SPOP rastgele bir üyeyi hem döndürür hem siler. SRANDMEMBER rastgele üye döndürür ama silmez. Belgelerdeki örnekte rastgele seçilen üyeler gösterilmiştir; kendi denemenizde farklı üyeler görebilirsiniz:

text
> DEL bikes:racing:france
(integer) 1
> SADD bikes:racing:france bike:1 bike:2 bike:3 bike:4 bike:5
(integer) 5
> SREM bikes:racing:france bike:1
(integer) 1
> SPOP bikes:racing:france
"bike:3"
> SMEMBERS bikes:racing:france
1) "bike:2"
2) "bike:4"
3) "bike:5"
> SRANDMEMBER bikes:racing:france
"bike:2"

SRANDMEMBER'a count verirseniz davranış işarete göre değişir. Pozitif count birbirinden farklı üyeler döndürür; count küme boyutundan büyükse bütün küme gelir ve yanıt sırası gerçekten rastgele değildir. Negatif count ile aynı üye birden çok kez gelebilir, ama tam olarak istenen sayıda üye döner ve sıra gerçekten rastgeledir. SPOP da count alır ve en fazla o kadar üyeyi silip döndürür. Belgeler SPOP'un garantili düzgün dağılım gerektiren işler için uygun olmadığını not eder.

SMOVE bir üyeyi bir kümeden diğerine atomik olarak taşır: diğer istemciler üyeyi her an ya kaynakta ya da hedefte görür, ikisinde birden ya da hiçbirinde görmez. Üye kaynakta yoksa 0 döner. Üye hedefte zaten varsa yalnızca kaynaktan silinir. Bu, örneğin bir görevi "bekleyen" kümesinden "tamamlanan" kümesine geçirmek için temiz bir yoldur.

Set'ler de list ve hash gibi birden fazla elemandan oluşan tiplerin ortak kurallarına uyar. Var olmayan bir anahtara SADD yaptığınızda küme kendiliğinden oluşur; SREM ya da SPOP son üyeyi de kaldırdığında anahtar otomatik olarak silinir. Var olmayan anahtar üzerinde SCARD 0, SREM 0, SISMEMBER 0 döndürür; yani boş küme ile hiç olmayan küme arasında okuma açısından fark yoktur. Anahtar başka bir tipte değer tutuyorsa SADD ve SREM hata döndürür. Bu hata belgelerde WRONGTYPE Operation against a key holding the wrong kind of value olarak geçer ve genellikle aynı anahtar adının iki farklı amaçla kullanıldığını gösterir.

Uygulamalı örnek: etiketlerle filtreleme

Bir blogda her etiket için bir set tuttuğunuzu düşünün. Her set o etiketi taşıyan yazıların kimliklerini içerir. Aşağıdaki komutlar belgelerde çıktısıyla gösterilmediği için sonuçlarını açıklama olarak veriyoruz:

text
SADD tag:redis post:1 post:2 post:5
SADD tag:linux post:2 post:3 post:5
SINTER tag:redis tag:linux
SINTERCARD 2 tag:redis tag:linux
SDIFF tag:redis tag:linux
SISMEMBER tag:linux post:1

İki SADD komutu üçer üye ekler ve her biri 3 döndürür. SINTER iki etikete de sahip olan post:2 ve post:5 üyelerini (sırası garanti olmadan) döndürür; SINTERCARD aynı sorunun yalnızca sayısını, yani 2 verir. SDIFF Redis etiketli olup Linux etiketli olmayan post:1 üyesini döndürür. Son komut 0 döner, çünkü post:1 Linux kümesinde yoktur. Yazıların başlık ve içerik gibi ek bilgilerini kümede değil, kimliğe karşılık gelen bir hash'te tutarsınız; belgeler set üyeleri için ek bilgiyi aynı adlı alanlara sahip yardımcı bir hash ya da JSON nesnesinde saklamayı önerir.

Performans, sınırlar ve alternatifler

Performans, sınırlar ve alternatifler: Kesin tekil üyelik ve küme işlemleri, Üyeleri puana göre sıralamak, Üyeler 0 ile N arası tamsayı, Yalnızca yaklaşık tekil sayı

Ekleme, silme ve üyelik testi dahil set işlemlerinin çoğu O(1)'dir. SMEMBERS ise O(n)'dir ve bütün kümeyi tek yanıtta döndürür; belgeler yüz binlerce ya da daha fazla üyeli kümelerde bu komutu dikkatli kullanmanızı, kümenin tamamını adım adım dolaşmak için SSCAN kullanmanızı önerir. İmleç mantığı SCAN ve KEYS farkı yazısında anlatılıyor.

Çok büyük veri kümelerinde ya da akan verilerde üyelik testi çok bellek tüketebilir. Kusursuz doğruluk gerekmiyorsa belgeler set yerine Bloom ya da Cuckoo filtresini önerir. Set'ler sık sık bir tür indeks olarak da kullanılır; verinizi indeksleyip sorgulamanız gerekiyorsa belgeler JSON tipini ve Redis Search'ü önerir. Birden çok anahtarla çalışan SINTER, SUNION, SDIFF, SMOVE ve kardeşleri cluster ortamında farklı davranabilir; bu konu hash slot ve CROSSSLOT yazısında ele alınıyor.

İhtiyaçÖnerilen yapıNot
Kesin tekil üyelik ve küme işlemleriSetÜyeleri tek tek saklar
Üyeleri puana göre sıralamakSorted setDaha fazla bellek ve işlem
Üyeler 0 ile N arası tamsayıBitmapÜye başına tek bit
Yalnızca yaklaşık tekil sayıHyperLogLogEn fazla 12 KB, yaklaşık sonuç
Yaklaşık üyelik testiBloom veya Cuckoo filtresiYanlış pozitif olabilir

Bu alternatiflerin ayrıntıları için bitmap ve bitfield, HyperLogLog ve Bloom ve Cuckoo filtresi yazılarına bakabilirsiniz.

Sık yapılan hatalar

  • Sıraya güvenmek: SMEMBERS, SINTER ve SUNION yanıtlarının sırası garanti değildir.
  • SDIFF argüman sırası: Fark işlemi değişmeli değildir; çıkarılacak küme ilk sırada değil, sonradadır.
  • Büyük kümede SMEMBERS: Bütün kümeyi tek yanıtta döndürür; SSCAN kullanın ya da yalnızca sayı gerekiyorsa SCARD/SINTERCARD tercih edin.
  • numkeys argümanını unutmak: SINTERCARD, SUNIONCARD ve SDIFFCARD ilk argüman olarak anahtar sayısını ister; SINTER istemez.
  • Yanlış yazılmış anahtar: Var olmayan anahtar boş küme sayılır ve kesişimi sessizce boşaltır.
  • Rastgelelik beklentisi: Pozitif count ile SRANDMEMBER yanıtının sırası gerçekten rastgele değildir; gerekiyorsa istemcide karıştırın.

Sık Sorulan Sorular

Set ile list arasındaki temel fark nedir?

List sıralıdır ve aynı değeri birden çok kez tutabilir; set sırasızdır ve her üyeyi bir kez tutar. List'te bir değeri aramak eleman sayısıyla orantılıyken set'te üyelik testi O(1)'dir. Liste komutları için Redis list komutları yazısına bakın.

SINTER ile SINTERCARD hangisini kullanmalıyım?

Üyelerin kendisine ihtiyacınız varsa SINTER, yalnızca sayıya ihtiyacınız varsa SINTERCARD kullanın. SINTERCARD sonucu istemciye taşımaz ve LIMIT ile erken durabilir.

SUNIONCARD APPROX ne kadar doğru?

Belgelere göre APPROX seçeneği içeride HyperLogLog kullanır ve standart hatası yaklaşık %0,81'dir. Kesin sayı gerekiyorsa seçeneği vermeyin; varsayılan davranış kesin sayımdır.

Bir set'te kaç üye tutabilirim?

En fazla 4.294.967.295 (2^32 - 1) üye. Bu kadar büyük kümelerde bellek kullanımı asıl sınırdır; yaklaşık sonuçlar yeterliyse olasılıksal yapıları değerlendirin.

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 · Redis sorted set · HyperLogLog · Bloom filtresi · SCAN ve SSCAN · Redis nedir, nasıl kullanılır