Redis'te istemci tarafı önbellek (client-side caching), istemci kütüphanesinin sunucudan okuduğu değerleri uygulamanın kendi belleğinde tutması ve aynı değer yeniden istendiğinde ağa çıkmadan yanıt vermesidir. Önbelleğin bayatlamasını Redis sunucusu engeller: CLIENT TRACKING ON ile izleme açılan bir bağlantının okuduğu anahtarları hatırlar ve bu anahtarlardan biri değiştiğinde, süresi dolduğunda veya bellek politikası yüzünden silindiğinde ilgili istemcilere bir geçersiz kılma (invalidation) mesajı gönderir. İstemci de bu mesajı alınca yerel kopyayı siler.
Bu yazı Redis belgelerindeki "Client-side caching introduction" ve "Client-side caching reference" sayfalarını ve CLIENT TRACKING komut sayfasını temel alır. Önbelleğin genel kavramları (isabet, ıskalama, geçersiz kılma) için önbellek nedir yazısına, sunucu tarafında bellek sınırı ve silme politikaları için maxmemory ve eviction yazısına bakabilirsiniz.
Neden istemci tarafında önbellek?
Varsayılan düzende uygulama sunucusu her okuma isteği için istemci kütüphanesi üzerinden Redis'e gider. İstemci tarafı önbellek açıkken kütüphane okuduğu değerleri yerel bir önbellekte tutar ve aynı öğeler tekrar gerektiğinde isteği önbellekten karşılar. Belge iki temel kazancı şöyle özetler: veriye çok düşük gecikmeyle erişilir ve veritabanı daha az sorgu aldığı için aynı veri kümesine daha az düğümle hizmet verebilir. Yerel belleğe erişmek, ağ üzerindeki bir servise erişmekten kat kat hızlıdır.
Her önbellek gibi bu da verinin küçük bir kısmının geri kalanından çok daha sık okunduğu durumda işe yarar. Referans sayfasının örneği bir sosyal ağdaki gönderilerdir: çoğu gönderi ya hiç değişmez ya da nadiren düzenlenir ve gönderilerin küçük bir kısmı çok popülerdir. Zor olan kısım geçersiz kılmadır. Uygulama user:1234 için kullanıcı adını yerelde tuttuktan sonra kullanıcı adını değiştirirse uygulama eski adı göstermeye devam edebilir. Sabit bir süre sonra önbelleği atmak bazen yeterlidir; Pub/Sub ile kendi geçersiz kılma mesajlarınızı göndermek ise belgeye göre çalıştırılabilir ama zahmetli ve bant genişliği açısından pahalıdır, çünkü mesajlar çoğu zaman o veriyi hiç tutmayan istemcilere de gider ve her yazmada ayrıca PUBLISH çalıştırmak gerekir. Redis 6 bu yüzden bu deseni doğrudan destekleyen izleme (tracking) özelliğini getirdi.
İzleme (tracking) nasıl çalışır?

Bağlantılar izleme kapalı başlar; istemci isterse açar. İzleme açıkken sunucu, bağlantının yaşam süresi boyunca okuma komutlarıyla istediği anahtarları hatırlar. Bu yalnızca GET gibi değeri doğrudan döndüren komutları değil, STRLEN gibi sunucunun saklanan veriden hesap yaptığı komutları da kapsar. Bir anahtar bir istemci tarafından değiştirildiğinde, süresi dolduğu için veya maxmemory politikası yüzünden silindiğinde, izleme açık olup anahtarı önbelleğinde tutuyor olabilecek tüm istemcilere geçersiz kılma mesajı gider. Referans sayfası akışı şöyle verir:
- İstemci 1 sunucuya
CLIENT TRACKING ONgönderir. - İstemci 1
GET foogönderir; sunucu, istemci 1'infooanahtarını önbelleğe almış olabileceğini not eder. - İstemci 2
SET foo SomeOtherValuegönderir. - Sunucu istemci 1'e
INVALIDATE "foo"mesajı gönderir; istemci 1 yerel kopyayı siler.
Sunucunun belleğini korumak için iki önemli tasarım kararı vardır. Birincisi, bir anahtarı hangi istemcilerin önbelleğe almış olabileceği tek bir genel tabloda, geçersiz kılma tablosunda (Invalidation Table) tutulur. Bu tablonun bir üst sınırı vardır; yeni bir anahtar eklenirken sunucu eski bir girişi, o anahtar değişmiş gibi davranıp geçersiz kılma mesajı göndererek tablodan çıkarabilir. Yani bazen değişmemiş bir anahtar için de geçersiz kılma alabilirsiniz. İkincisi, tabloda istemci yapılarına işaretçi değil yalnızca istemci kimlikleri tutulur; bağlantısı kopan istemcilerin bilgisi zamanla temizlenir.
Üçüncü bir ayrıntı şaşırtıcı olabilir: izleme için anahtar ad alanı veritabanı numaralarına bölünmez. Bir istemci 2 numaralı veritabanındaki foo anahtarını önbelleğe almışken başka bir istemci 3 numaralı veritabanındaki foo anahtarını değiştirirse geçersiz kılma mesajı yine gönderilir. Bu, bellek kullanımını ve uygulama karmaşıklığını azaltmak için bilinçli bir tercihtir; farklı veritabanlarında aynı anahtar adlarını yoğun kullanıyorsanız gereksiz geçersiz kılmalar görebilirsiniz.
RESP3 ile tek bağlantı, RESP2 ile iki bağlantı
RESP3 protokolüyle veri sorguları ve geçersiz kılma mesajları aynı bağlantıda taşınabilir; mesajlar RESP3'ün "push" türünde gelir. Yine de birçok istemci iki ayrı bağlantı kullanmayı tercih edebilir: biri veri, biri geçersiz kılma için. Bunun için izleme açılırken REDIRECT ile mesajların hangi bağlantıya gönderileceği, o bağlantının kimliği (CLIENT ID ile alınır) verilerek belirtilir. Birçok veri bağlantısı aynı geçersiz kılma bağlantısına yönlendirilebilir; bu, bağlantı havuzu kullanan istemciler için yararlıdır. RESP2'de desteklenen tek model budur, çünkü RESP2 aynı bağlantıda farklı türde bilgiyi çoklayamaz.
RESP2 düzeninde geçersiz kılma bağlantısı özel __redis__:invalidate kanalına abone olur. Referans sayfasındaki ham protokol oturumunda önce geçersiz kılma bağlantısı kimliğini öğrenip kanala abone olur, sonra veri bağlantısı izlemeyi bu kimliğe yönlendirir:
(Baglanti 1 - gecersiz kilma icin)
CLIENT ID
:4
SUBSCRIBE __redis__:invalidate
(Baglanti 2 - veri baglantisi)
CLIENT TRACKING on REDIRECT 4
+OK
GET foo
$3
barBaşka bir bağlantı foo anahtarını değiştirdiğinde 1 numaralı bağlantıya bir Pub/Sub mesajı gelir. Mesajın üçüncü elemanı tek bir anahtar değil, tek elemanlı bir dizidir; böylece birden çok anahtar tek mesajda geçersiz kılınabilir. FLUSHALL veya FLUSHDB durumunda boş (null) bir mesaj gönderilir ve istemci tüm önbelleği atmalıdır. Belgenin altını çizdiği nokta, burada Pub/Sub'ın yalnızca eski istemci uygulamalarını yeniden kullanmak için bir hile olduğudur: mesaj gerçekten kanala abone herkese gitmez, yalnızca REDIRECT ile belirtilen bağlantıya gider. Redis Pub/Sub'ın kendisi için Pub/Sub yazısına bakın.
İzleme modları: varsayılan, OPTIN, OPTOUT, BCAST

CLIENT TRACKING komutu zorunlu olarak ON veya OFF alır; izleme açıldıktan sonra OFF ile kapatılana kadar bağlantının ömrü boyunca açık kalır. İsteğe bağlı seçenekler izlemenin nasıl yapılacağını belirler:
| Seçenek | Davranış | Sunucu belleği |
|---|---|---|
| Varsayılan | Okuma komutlarındaki tüm anahtarlar izlenir | Anahtar ve istemci sayısıyla orantılı |
| OPTIN | Yalnızca CLIENT CACHING YES sonrasındaki komut izlenir | Daha az |
| OPTOUT | Her şey izlenir, CLIENT CACHING NO sonrasındaki komut hariç | Varsayılana yakın |
| BCAST | Okunan anahtarlar değil, öneki eşleşen tüm anahtarlar için mesaj | Yok, önek tablosu kullanılır |
| NOLOOP | Bağlantının kendi değiştirdiği anahtarlar için mesaj gelmez | Değişmez |
| REDIRECT id | Mesajlar başka bir bağlantıya gönderilir | Değişmez |
OPTIN ve OPTOUT
Varsayılan modda istemcinin sunucuya neyi önbelleğe aldığını söylemesine gerek yoktur; salt okuma bir komutta geçen her anahtar "önbelleğe alınmış olabilir" diye izlenir. Bu, gelen her yeni nesneyi önbelleğe alan istemciler için verimlidir, ama sunucuyu daha fazla bilgi tutmaya zorlar ve istemciye önbelleğe almadığı nesneler için de gereksiz mesajlar gönderir. OPTIN modunda ise okunan anahtarlar varsayılan olarak izlenmez; istemci bir şeyi önbelleğe almak istediğinde komuttan hemen önce CLIENT CACHING YES gönderir:
CLIENT TRACKING ON REDIRECT 1234 OPTIN
CLIENT CACHING YES
+OK
GET foo
"bar"CLIENT CACHING yalnızca kendisinden hemen sonraki komutu etkiler. İstisnalar belgede yazılıdır: sonraki komut MULTI ise transaction içindeki tüm komutlar, Lua betiği ise betiğin çalıştırdığı tüm komutlar izlenir. OPTOUT modu tersini yapar: her şey izlenir, CLIENT CACHING NO ile işaretlediğiniz bir sonraki komut hariç. Bağlantının hangi modda olduğunu CLIENT TRACKINGINFO ile görebilirsiniz; komut on, bcast, optin, noloop veya yönlendirme bağlantısı geçersizleştiyse broken_redirect gibi bayrakları, yönlendirme kimliğini ve önekleri döndürür.
Yayın (BCAST) modu
Yayın modunda sunucu hangi istemcinin hangi anahtarı okuduğunu hatırlamaz, bu yüzden sunucu tarafında bellek tüketmez. İstemci bunun yerine anahtar öneklerine abone olur ve öneki eşleşen bir anahtar her değiştiğinde mesaj alır: CLIENT TRACKING on REDIRECT 10 BCAST PREFIX object: PREFIX user:. Hiç önek verilmezse önek boş dizge sayılır ve istemci değişen her anahtar için mesaj alır. Örtüşen önekler kullanılamaz; "foo" ve "foob" birlikte verilemez, çünkü ikisi de "foobar" için tetiklenir, yalnızca "foo" yeterlidir. Tek bir öneki kaldıramazsınız; tüm önekleri kaldırmak için izlemeyi kapatıp yeniden açmanız gerekir.
Bedeli CPU tarafındadır: sunucu kayıtlı önek sayısıyla orantılı CPU harcar. Birkaç önekte fark hissedilmez, çok sayıda önekte maliyet belirgin biçimde artabilir; komut sayfası PREFIX seçeneğinin toplam izlenen önek sayısına göre O(N^2) karmaşıklık eklediğini belirtir. Avantajı da vardır: aynı öneke abone tüm istemciler için tek bir yanıt oluşturulup hepsine gönderilebilir. Anahtar adlarınızı nesne:id düzeninde tutarlı kurduysanız (bkz. anahtar isimlendirme) önek seçimi çok daha anlamlı olur.
NOLOOP
Varsayılan olarak izleme, anahtarı değiştiren istemcinin kendisine de geçersiz kılma mesajı gönderir. Kendi yazdığı değerleri de yerel önbelleğe koyan gelişmiş istemciler için bu sorundur, çünkü yazdıktan hemen sonra gelen mesaj az önce önbelleğe aldığı değeri sildirir. NOLOOP bunu engeller ve hem varsayılan hem yayın modunda çalışır. Belgedeki ince ayrıntı şudur: varsayılan modda anahtar değiştiğinde geçersiz kılma tablosundan çıkarılır; NOLOOP mesajı bastırsa bile anahtar o bağlantı için artık izlenmez. Gelecekteki değişikliklerden haberdar olmak için bağlantının anahtarı yeniden okuması gerekir.
redis-py ile istemci tarafı önbellek
Belgeye göre redis-py 5.1.0, Jedis 5.2.0, node-redis 5.1.0 ve go-redis 9.22.0 sürümlerinden itibaren istemci tarafı önbelleği destekler. Belge, bazı başka istemcilerin CLIENT TRACKING komutunu sunucuda yapılandırabildiğini, ancak bunun o istemcilerin önbellek için gereken özellikleri kendisinin desteklediği anlamına gelmediğini de not eder. redis-py'de önbelleği açmak için bağlantıya iki parametre eklenir: RESP3 zorunlu olduğu için protocol=3 ve cache_config=CacheConfig():
import redis
from redis.cache import CacheConfig
r = redis.Redis(
protocol=3,
cache_config=CacheConfig(),
decode_responses=True
)
r.set("city", "New York")
cityNameAttempt1 = r.get("city") # Sunucudan okunur ve onbellege alinir
cityNameAttempt2 = r.get("city") # Onbellekten okunurredis-py sayfası, en geniş uyumluluk için istemci tarafı önbelleğin Redis 7.4 veya sonrasıyla desteklendiğini ve OPTIN/OPTOUT ile yayın modunun şu an redis-py tarafından uygulanmadığını belirtir. Aynı parametreler bağlantı havuzu ve cluster bağlantısında da aynı şekilde kullanılabilir. Önbellekteki tek tek anahtarları r.get_cache().delete_by_redis_keys(["person:1"]) ile, tamamını flush() ile temizleyebilirsiniz; anahtar bazlı silme, o anahtarla ilişkili tüm sonuçları (örneğin MGET sonuçları ve hash'lerin tüm alanları) birlikte atar. Bağlantı ayarlarının geri kalanı için redis-py yazısına bakın.
Önbelleğin çalıştığını doğrulamak için belge, aynı veritabanına redis-cli ile bağlanıp MONITOR çalıştırmayı önerir. Önbellek kapalıyken her iki get("city") çağrısı da sunucuda görünür; açıkken yalnızca ilki görünür, ikincisi yerel önbellekten karşılanır.
Hangi komutlar önbelleğe alınır, hangileri alınmaz?
Belgeye göre @read ACL kategorisindeki tüm salt okuma komutları önbelleği kullanır; şu istisnalar dışında:
- Olasılıksal yapılar ve zaman serisi komutları. Bu tipler sık güncellenmek için tasarlandığından önbelleğin faydası azdır veya hiç yoktur.
HRANDFIELD,HSCANveZRANDMEMBERgibi belirlenimsiz komutlar; her çağrıda farklı sonuç vermeleri tasarımlarının gereğidir.FT.SEARCHgibiFT.*önekli Redis Search komutları.
Neyin ayrı bir önbellek girdisi olduğu da önemlidir. Genel kural, bir komut çağrısının yanıtının ilk kullanımda önbelleğe alınması, bu verinin alt kümelerinin ve ondan hesaplanan değerlerin ise sunucudan ayrıca alınıp ayrı girdiler olarak önbelleğe alınmasıdır. GET ile alınan dizge bir girdidir, aynı dizgenin SUBSTR ile alınan parçası başka bir girdidir. Hash, JSON, set ve sorted set gibi bileşik tiplerde nesnenin tamamı tek tek alanlardan ayrı tutulur: JSON.GET mykey $ ile JSON.GET mykey $.myfield iki ayrı girdidir. Liste, stream ve sorted set aralıkları da ayrı girdilerdir. Çok anahtarlı okumalarda sıra önemlidir: MGET name:1 name:2 ile MGET name:2 name:1 farklı girdilerdir. SISMEMBER veya LLEN gibi hesaplanmış değerler de nesneden ayrı önbelleğe alınır.
Yarış durumu ve bağlantı kopması
Geçersiz kılma mesajlarını ayrı bir bağlantıya yönlendirdiğinizde bir yarış durumu mümkündür. Veri bağlantısında GET foo gönderirsiniz, bu arada başka biri anahtarı değiştirir ve geçersiz kılma mesajı geçersiz kılma bağlantısından, GET yanıtından önce gelir. Ardından gelen eski yanıtı önbelleğe koyarsanız bayat veriyi sunmaya devam edersiniz. Belgenin önerdiği çözüm, komutu gönderirken önbelleğe bir yer tutucu ("caching-in-progress") koymaktır: geçersiz kılma gelirse yer tutucu silinir, yanıt geldiğinde girdi artık olmadığı için değer önbelleğe yazılmaz. Veri ve geçersiz kılma tek bağlantıdaysa mesajların sırası bilindiği için bu yarış oluşmaz.
Geçersiz kılma mesajlarını aldığınız bağlantı koparsa da bayat veri riski doğar. Belge iki önlem sayar: bağlantı koptuğunda yerel önbelleği tamamen boşaltmak ve RESP2 Pub/Sub ya da RESP3 kullanıyor olmanız fark etmeksizin geçersiz kılma bağlantısına periyodik olarak PING göndermek. Pub/Sub kipindeki bir bağlantıya da PING gönderilebilir. Yanıt gelmezse belirli bir süreden sonra bağlantıyı kapatıp önbelleği boşaltın. Giriş sayfası, havuzdaki dahil herhangi bir bağlantı koparsa destekleyen istemcilerin önbellekteki tüm anahtarları attığını ve önbelleğe almanın hâlâ açık olan bağlantılardaki sonraki okumalarla devam ettiğini belirtir.
Neyi önbelleğe almalı ve ne kadar?
Belgenin ölçütü açıktır: sürekli değişen anahtarları önbelleğe almak istemezsiniz, çok nadir istenen anahtarları da istemezsiniz; sık istenen ve makul bir hızla değişen anahtarlar idealdir. Sürekli INCR edilen genel bir sayaç, makul hızla değişmeyen anahtara iyi bir örnektir. Giriş sayfası buradan pratik bir öneri çıkarır: sayaçlar ve skor tabloları gibi sık güncellenen veriler için geçersiz kılma mesajlarının maliyeti önbellek kazancını aşabilir, bu yüzden bu veriler için istemci tarafı önbelleği olmayan ayrı bir bağlantı kullanın. Sık güncellenen bir sıralama tablosu tasarlıyorsanız leaderboard yazısı bu açıdan iyi bir karşı örnektir.
Önbelleğe kaç öğe sığacağını tahmin etmek için belge basit bir hesap önerir: önbellek için ayırdığınız en büyük bellek miktarını, saklayacağınız öğelerin ortalama boyutuna bölün. Ortalama boyutu MEMORY USAGE ile ölçebilirsiniz (bkz. bellek kullanımı yazısı). Belgenin örneğinde 10 MB (10 485 760 bayt) ayrılır ve ortalama öğe 80 bayttır; yaklaşık 10485760 / 80 = 131 072 öğe sığar. Bu tahmini gerçekçi bir test yüküyle sunucudaki bellek kullanımını izleyerek yukarı veya aşağı ayarlayın.
Kendi istemcinizi yazıyorsanız referans sayfasının ek ipuçları vardır. Süreli anahtarları destekleyecekseniz anahtarın TTL değerini de isteyip yerel önbellekte aynı süreyi uygulayın. Süresi olmayan anahtarlara bile yerelde bir üst süre koymak iyi bir fikirdir; bu, hatalara veya bağlantı sorunlarına karşı koruma sağlar. İstemcinin kullandığı belleği sınırlamak da kesinlikle gereklidir; yeni anahtar eklendiğinde eskileri atmanın bir yolu olmalıdır. Sunucu tarafında ise varsayılan modda tüketilen bellek izlenen anahtar ve bu anahtarları isteyen istemci sayısıyla orantılıdır; belge Redis'in hatırlayacağı anahtar sayısı için uygun bir üst sınır yapılandırmayı veya sunucuda hiç bellek tüketmeyen BCAST modunu kullanmayı önerir.
Sık yapılan hatalar

- RESP3'ü açmamak: redis-py gibi istemcilerde önbellek RESP3 gerektirir;
protocol=3olmadan çalışmaz. - Sık değişen veriyi önbelleğe almak: Sayaçlar ve skorlar için geçersiz kılma trafiği kazancı yer; bunlar için ayrı, önbelleksiz bağlantı kullanın.
- Değişmeyen anahtar için gelen geçersiz kılmaya şaşırmak: Geçersiz kılma tablosu dolduğunda sunucu eski girdileri sanki değişmiş gibi geçersiz kılar; ayrıca izleme veritabanı numaralarını ayırt etmez.
- BCAST'te örtüşen önek vermek: "foo" ile "foob" birlikte kullanılamaz.
- Bağlantı koptuğunda önbelleği korumak: Kopma sırasında kaçırılan geçersiz kılmalar yüzünden önbellek bayat olabilir; boşaltın.
- Üretimde MONITOR ile uzun süre izlemek: İşlem hacmini ciddi biçimde düşürür.
Sık Sorulan Sorular
İstemci tarafı önbellek için sunucuda ayar gerekir mi?
İzleme bağlantı başına açılır; destekleyen istemci kütüphaneleri bunu bağlantı kurulurken kendisi yapar. Sizin yapmanız gereken, uygun istemci sürümünü kullanmak ve RESP3 ile bağlanmaktır. redis-py belgesi en geniş uyumluluk için Redis 7.4 veya sonrasını belirtir.
Önbellek ile TTL arasındaki ilişki nedir?
Süresi dolan bir anahtar için sunucu da geçersiz kılma mesajı gönderir. Kendi istemcinizi yazıyorsanız referans sayfası anahtarın TTL değerini isteyip yerelde de uygulamanızı ve süresiz anahtarlara bile yerel bir üst süre koymanızı önerir. Sunucu tarafı süreler için EXPIRE ve TTL yazısına bakın.
Bağlantı havuzuyla kullanılabilir mi?
Evet. Birden çok veri bağlantısı geçersiz kılma mesajlarını REDIRECT ile aynı bağlantıya yönlendirebilir; belge bunun havuz kullanan istemciler için yararlı olduğunu belirtir. redis-py'de aynı parametreler havuzlu bağlantıda da geçerlidir. Havuzlar hakkında genel bilgi bağlantı URL ve connection pool yazısında.
Arama ve olasılıksal komutlar neden önbelleğe alınmıyor?
Belge olasılıksal ve zaman serisi tiplerinin sık güncellenmek için tasarlandığını, bu yüzden önbelleğin fayda sağlamadığını söyler; FT.* komutları ve belirlenimsiz komutlar da açıkça istisna tutulur.
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 · Önbellek nedir · redis-py kullanımı · Redis Pub/Sub · maxmemory ve eviction · Bağlantı ve connection pool