Redis'in tüm hızı verisini bellekte tutmasından gelir ve bu, bellek dolduğunda ne olacağının önceden karar verilmiş olmasını gerektirir. Karar verilmemişse iki kötü sonuçtan biriyle karşılaşırsınız: ya Redis OOM command not allowed when used memory > 'maxmemory' hatası döndürüp yazma kabul etmeyi bırakır, ya da maxmemory hiç ayarlanmadığı için sınırsız büyüyüp işletim sisteminin OOM killer'ı tarafından öldürülür. Redis bellek optimizasyonu, tam olarak bu iki sondan kaçınmayı ve aynı RAM ile daha fazla veri tutmayı konu alır.
Bu rehberde maxmemory ayarının nasıl seçileceğini, sekiz eviction politikasının hangi durumda hangisinin doğru olduğunu, bellek kullanımını gerçekten neyin tükettiğini MEMORY USAGE ile ölçmeyi, veri yapısı ve anahtar adlandırma seçimlerinin ne kadar yer kazandırdığını, fragmentasyon sorununu ve activedefrag ayarını, son olarak da bellek dolmadan önce fark etmenizi sağlayacak izleme metriklerini anlatacağım.
maxmemory Ayarını Doğru Seçmek#
Varsayılan Redis yapılandırmasında maxmemory ayarlı değildir; yani Redis bellek dolana kadar büyür. Bu, önbellek olarak kullanılan bir Redis için felakete davetiyedir çünkü sınır işletim sistemi tarafından, en kötü anda ve en sert biçimde konur.
Sınırı belirlerken tek başına veri boyutunu düşünmek yetmez. Redis, veri kümesinin dışında da bellek kullanır: istemci çıktı tamponları, replikasyon tamponu, AOF tamponu ve en önemlisi kalıcılık sırasındaki fork() maliyeti. Bu yüzden pratik kural şudur: maxmemory değerini fiziksel RAM'in yaklaşık yarısı ile üçte ikisi arasında tutun.
# /etc/redis/redis.conf
# 16 GB RAM'li bir makinede makul bir başlangıç
maxmemory 8gb
maxmemory-policy allkeys-lru
# Örnekleme sayısı: LRU/LFU tahmininin doğruluğunu belirler
maxmemory-samples 5
# Replika, eviction kararını master'dan alsın (varsayılan)
replica-ignore-maxmemory yes
Kalıcılık kapalıysa (save "" ve appendonly no) fork() yükü de olmaz ve sınırı RAM'in yüzde 75'ine kadar çıkarabilirsiniz. Kalıcılık açıksa yarıda kalın; snapshot sırasında yazma yoğunsa copy-on-write yüzünden bellek kullanımı ciddi biçimde sıçrayabilir. Bu ilişkinin ayrıntısı için Redis persistence: RDB ve AOF farkı yazısına bakabilirsiniz.
Mevcut kullanımı görmek için:
redis-cli INFO memory | grep -E 'used_memory_human|used_memory_rss_human|maxmemory_human|mem_fragmentation_ratio|maxmemory_policy'
used_memory_human:6.42G
used_memory_rss_human:7.10G
maxmemory_human:8.00G
mem_fragmentation_ratio:1.11
maxmemory_policy:allkeys-lru
used_memory Redis'in kendi hesabıyla tuttuğu veri miktarıdır; used_memory_rss ise işletim sisteminin bu sürece gerçekten ayırdığı fiziksel bellektir. İkisi arasındaki oran fragmentasyon hakkında bilgi verir ve birazdan ayrı bir bölümde ele alacağım.
Sekiz Eviction Politikası#
maxmemory sınırına ulaşıldığında ne olacağını maxmemory-policy belirler. Sekiz seçenek vardır ve yanlış seçim ya veri kaybına ya da hizmet kesintisine yol açar.
| Politika | Ne yapar | Ne zaman kullanılır |
|---|---|---|
noeviction | Hiçbir şey silmez, yazmayı reddeder | Redis kalıcı veri deposu olarak kullanılıyorsa |
allkeys-lru | Tüm anahtarlardan en az kullanılanı siler | Saf önbellek — en yaygın doğru seçim |
allkeys-lfu | Tüm anahtarlardan en seyrek kullanılanı siler | Erişim sıklığı çok değişken önbelleklerde |
allkeys-random | Rastgele anahtar siler | Erişim deseni tamamen düzgün dağılmışsa |
volatile-lru | Yalnızca TTL'li anahtarlardan LRU siler | Karma kullanım: kalıcı + geçici veri |
volatile-lfu | Yalnızca TTL'li anahtarlardan LFU siler | Aynı, sıklık odaklı |
volatile-random | Yalnızca TTL'li anahtarlardan rastgele siler | Nadiren |
volatile-ttl | TTL'i en yakın olanı önce siler | Süresi dolmak üzere olanı erken atmak isteniyorsa |
Varsayılan politika noeviction'dır ve bu, önbellek olarak kullanılan bir Redis için neredeyse her zaman yanlıştır: bellek dolduğunda uygulamanız OOM hatası almaya başlar. Önbellek kullanıyorsanız allkeys-lru ile başlayın.
volatile-* ailesinin sinsi tuzağı şudur: TTL'i olmayan anahtar yoksa silinecek bir şey de yoktur. volatile-lru seçtiniz ama uygulamanız SET komutlarına EX eklemeyi unuttuysa, bellek dolduğunda Redis silecek aday bulamaz ve noeviction gibi davranıp yazmayı reddeder. Bu politikayı seçmeden önce anahtarlarınızın gerçekten TTL taşıdığını doğrulayın:
# TTL'i olmayan (kalıcı) anahtar sayısına dair fikir verir
redis-cli INFO keyspace
# db0:keys=1204583,expires=1198420,avg_ttl=3421000
keys ile expires sayıları birbirine yakınsa çoğu anahtarın TTL'i var demektir. Aradaki fark büyükse volatile-* politikaları güvenli değildir.
LRU ile LFU arasındaki seçim şuna bakar: LRU en son ne zaman kullanıldığına, LFU ne kadar sık kullanıldığına bakar. Bir anahtar günde binlerce kez okunuyor ama son beş dakikada hiç okunmadıysa LRU onu atar, LFU tutar. Erişim deseninde belirgin bir "sıcak küme" varsa LFU daha iyi isabet oranı verir.
# Politikayı yeniden başlatmadan değiştirmek (kalıcı olması için conf'a da yazın)
redis-cli CONFIG SET maxmemory-policy allkeys-lfu
redis-cli CONFIG REWRITE
Belleği Gerçekte Ne Tüketiyor#
Optimizasyona başlamadan önce ölçün. Redis, hangi anahtarın ne kadar yer kapladığını doğrudan söyleyebilir:
# Tek bir anahtarın bellek maliyeti (bayt)
redis-cli MEMORY USAGE oturum:a1b2c3d4
# Genel bellek dökümü: veri, tamponlar, ek yükler
redis-cli MEMORY DOCTOR
redis-cli INFO memory
Veri kümesinin dağılımını görmek için redis-cli --bigkeys çok işe yarar; tarama yaparak her veri tipindeki en büyük anahtarı bulur ve bunu üretimde güvenle çalıştırabilirsiniz çünkü SCAN kullanır, sunucuyu bloklamaz:
redis-cli --bigkeys
# Örnek çıktı özeti:
# [00.00%] Biggest hash found so far '"urun:katalog"' with 84213 fields
# [12.40%] Biggest string found so far '"rapor:gunluk"' with 4194304 bytes
Bellek dağılımını anahtar öneklerine göre görmek isterseniz --memkeys seçeneği veya örnekleme tabanlı bir tarama kullanılabilir. Asıl mesele şudur: Redis'te bellek maliyetinin büyük bir kısmı verinin kendisi değil, her anahtar için ödenen sabit ek yüktür. Bir anahtar, kendi adının uzunluğuna ek olarak sözlük girdisi, nesne başlığı ve TTL kaydı gibi yapılar için onlarca bayt harcar. Bu yüzden bir milyon küçük anahtar, aynı veriyi taşıyan bin hash'ten çok daha fazla yer kaplar.
Veri Yapısı ve Anahtar Adlandırma ile Kazanç#
Redis, küçük koleksiyonları bellek açısından verimli, kompakt biçimlerde saklar. Bir hash, liste veya sorted set belirli eşiklerin altındaysa "listpack" adı verilen düz bir yapıda tutulur; eşik aşıldığında gerçek hash tablosuna veya skip list'e dönüşür ve bellek kullanımı belirgin biçimde artar.
# Bu eşiklerin altındaki koleksiyonlar kompakt biçimde saklanır
hash-max-listpack-entries 128
hash-max-listpack-value 64
list-max-listpack-size 128
set-max-intset-entries 512
zset-max-listpack-entries 128
zset-max-listpack-value 64
Bu bilgi pratik bir tekniğe dönüşür: çok sayıda küçük anahtarı, az sayıda hash içinde gruplayın. Bir milyon oturum:<id> anahtarı yerine, id'nin bir parçasına göre gruplanmış hash'ler kullanmak bellek kullanımını kayda değer ölçüde düşürür.
# Verimsiz: her oturum ayrı anahtar, her biri sabit ek yük ödüyor
SET oturum:100001 "..."
SET oturum:100002 "..."
# Verimli: id'nin son üç hanesine göre gruplama, hash içinde alan
HSET oturum:100 "001" "..." "002" "..."
Anahtar adları da doğrudan bellek maliyetidir ve genelde göz ardı edilir. kullanici_oturum_bilgisi:profil:detay:100001 yerine u:o:100001 kullanmak, milyonlarca anahtarda yüzlerce megabayt fark eder. Okunabilirlikten tamamen vazgeçmeyin ama gereksiz tekrarları kısaltın.
Birkaç ek teknik:
- Sayısal değerleri string olarak değil, mümkünse tam sayı olarak saklayın; Redis tam sayıları özel ve daha kompakt biçimde tutar.
- Büyük JSON belgelerini tek string olarak tutmak yerine hash alanlarına ayırın; hem kısmi okuma yapabilir hem kompakt biçimden yararlanabilirsiniz. JSON yapısını incelemek için JSON formatlayıcı aracımız işinizi kolaylaştırır.
- Her anahtara mutlaka TTL koyun. TTL'siz bir önbellek anahtarı, silinmesi için başka bir mekanizmaya bağımlı kalır ve genelde o mekanizma hiç yazılmaz.
- Sayma ve benzersiz sayım işlerinde set yerine HyperLogLog (
PFADD/PFCOUNT) kullanın; milyonlarca benzersiz öğeyi yaklaşık 12 KB'da sayar.
Fragmentasyon ve activedefrag#
mem_fragmentation_ratio, used_memory_rss bölü used_memory oranıdır ve işletim sisteminin Redis'e ayırdığı belleğin, Redis'in gerçekten kullandığından ne kadar fazla olduğunu gösterir.
| Oran | Anlamı | Yapılacak |
|---|---|---|
| ~1.0 - 1.4 | Normal | Bir şey yapmayın |
| > 1.5 | Ciddi fragmentasyon | activedefrag açın veya yeniden başlatın |
| < 1.0 | Bellek diske takas ediliyor | Acil: swap kullanımını durdurun |
Oranın 1'in altına düşmesi en tehlikeli durumdur: Redis'in belleğinin bir kısmı diske takas edilmiş demektir ve bu, gecikmeleri milisaniyelerden yüz milisaniyelere çıkarır. Redis sunucularında swappiness değerini düşürmek veya swap'i tamamen kapatmak yaygın bir pratiktir.
Yüksek fragmentasyon, çok sayıda farklı boyutta anahtarın sürekli yazılıp silinmesiyle oluşur; bellek ayırıcısı serbest bıraktığı blokları işletim sistemine geri veremez. Redis'in aktif birleştirme özelliği bunu çalışırken düzeltir:
# Aktif defragmentasyon (jemalloc ile derlenmiş yapılarda çalışır)
activedefrag yes
# Bu eşiklerin altında hiç uğraşma
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
# CPU bütçesi: alt ve üst sınır (yüzde)
active-defrag-cycle-min 5
active-defrag-cycle-max 25
Aktif defrag CPU harcar, bu yüzden eşikleri makul tutun. active-defrag-cycle-max değerini 75 gibi yüksek bir sayıya çekmek, birleştirme sırasında Redis'in gecikmelerini belirgin biçimde artırır. Ayarı açtıktan sonra mem_fragmentation_ratio değerini birkaç saat izleyin; düşmüyorsa sorun fragmentasyon değil, gerçekten büyümüş bir veri kümesidir.
İzleme ve Sık Yapılan Hatalar#
Bellek sorunlarını dolmadan önce fark etmek için izlemeniz gereken metrikler bellidir:
# Kritik metrikleri tek satırda topla
redis-cli INFO | grep -E 'used_memory:|maxmemory:|evicted_keys|expired_keys|mem_fragmentation_ratio|blocked_clients|rejected_connections'
evicted_keys sayacı, bellek dolduğu için kaç anahtarın atıldığını gösterir. Bu sayı sürekli artıyorsa ya maxmemory yetersizdir ya da TTL'leriniz gereğinden uzundur. expired_keys ise TTL dolduğu için normal yoldan silinenlerdir ve artması sağlıklıdır.
En sık yapılan hatalar şunlar:
maxmemory ayarlamamak. Sınırsız büyüyen Redis, işletim sistemi tarafından öldürülür ve bellekteki her şey kaybolur. Kalıcılık açık olsa bile son snapshot'tan sonraki veri gider.
Önbellekte noeviction bırakmak. Varsayılan politika budur ve önbellek senaryosunda yanlıştır. Bellek dolduğunda uygulamanız OOM hatası almaya başlar; oysa istediğiniz şey eski anahtarların sessizce atılmasıdır.
TTL'siz anahtarlarla volatile-lru kullanmak. Silinecek aday bulunamadığında politika noeviction gibi davranır. Bu kombinasyon, "eviction politikası ayarlı ama yine OOM alıyorum" şikâyetinin en yaygın sebebidir.
Üretimde KEYS * çalıştırmak. Bu komut tüm anahtar alanını tarar ve tarama boyunca Redis'i tamamen bloklar. Milyonlarca anahtarlı bir örnekte saniyelerce süren tam bir kesinti yaratır. Yerine daima SCAN kullanın:
# Bloklamaz, imleçle parça parça ilerler
redis-cli --scan --pattern 'oturum:*' --count 1000 | head -20
Tek bir dev anahtar tutmak. Yüz binlerce elemanlı bir liste veya hash, hem belleği hem gecikmeyi bozar; o anahtara yapılan DEL işlemi bile sunucuyu bloklayabilir. Büyük anahtarları parçalayın; silmeniz gerekirse UNLINK kullanın, çünkü o silme işini arka plan iş parçacığına devreder.
Swap kullanımını fark etmemek. mem_fragmentation_ratio 1'in altındaysa Redis takas alanına düşmüştür ve performans çökmüştür. Bunu INFO memory çıktısında görmeden önce uygulamanızın gecikmelerinde hissedersiniz.
Sıkça Sorulan Sorular#
maxmemory değerini kaça ayarlamalıyım#
Kalıcılık kapalıysa fiziksel RAM'in yaklaşık dörtte üçü, açıksa yarısı iyi bir başlangıçtır. Aradaki fark fork() maliyetinden gelir: snapshot veya AOF yeniden yazma sırasında copy-on-write yüzünden bellek kullanımı geçici olarak ciddi biçimde artabilir. Redis dışında aynı makinede başka servisler çalışıyorsa payı ona göre düşürün. Ayarladıktan sonra evicted_keys sayacını izleyin; sürekli artıyorsa sınır dar demektir.
allkeys-lru mu volatile-lru mu kullanmalıyım#
Redis'te tuttuğunuz her şey önbellekse ve hepsi atılabilirse allkeys-lru doğru seçimdir; silinecek aday bulamama riski yoktur. Aynı örnekte hem atılabilir önbellek hem atılmaması gereken kalıcı veri tutuyorsanız volatile-lru kullanın, ama önbellek anahtarlarınızın gerçekten TTL taşıdığından emin olun. Aslında en temiz çözüm, iki farklı amacı iki farklı Redis örneğine ayırmaktır.
Redis OOM hatası veriyor, ne yapmalıyım#
Önce INFO memory çıktısına bakın: used_memory gerçekten maxmemory sınırına dayanmış mı? Dayanmışsa üç seçeneğiniz var: sınırı yükseltmek, eviction politikasını allkeys-lru yapmak veya veri kümesini küçültmek. Politikanız volatile-* ise ve anahtarlarınızın çoğunda TTL yoksa Redis silecek aday bulamıyordur; bu durumda politikayı allkeys-lru yapmak sorunu anında çözer. --bigkeys ile birkaç dev anahtarın yer kaplayıp kaplamadığını da kontrol edin.
Bir anahtarın kaç bayt yer kapladığını nasıl öğrenirim#
MEMORY USAGE anahtar_adi komutu, o anahtarın ek yüklerle birlikte toplam bellek maliyetini bayt cinsinden döndürür. Koleksiyon tipleri için tüm elemanları taramak yerine örnekleme yapar; kesin sonuç istiyorsanız SAMPLES 0 ekleyebilirsiniz ama büyük anahtarlarda bu pahalıdır. Genel bir tarama için redis-cli --bigkeys komutu her veri tipindeki en büyük anahtarı bloklamadan bulur.
Fragmentasyon oranı yüksek çıkıyor, tehlikeli mi#
1.5'in üzerindeki bir oran, işletim sisteminin Redis'e ayırdığı belleğin gerçek veri boyutunun yüzde elli fazlası olduğu anlamına gelir; bu, boşa harcanan RAM'dir. activedefrag yes ayarını açıp birkaç saat izleyin, oran genelde düşer. Oranın 1'in altına inmesi ise çok daha ciddidir: Redis'in belleği diske takas ediliyor demektir ve gecikmeler ölçülemez hale gelir. Bu durumda swap kullanımını acilen kesin.
Aynı veriyi daha az bellekte nasıl tutabilirim#
Dört teknik belirgin kazanç sağlar: çok sayıda küçük anahtarı hash'ler içinde gruplayın çünkü anahtar başına sabit bir ek yük ödenir, anahtar adlarını kısaltın, sayısal değerleri tam sayı olarak saklayın ve benzersiz sayım işlerinde set yerine HyperLogLog kullanın. Ayrıca koleksiyonlarınızı hash-max-listpack-entries gibi eşiklerin altında tutmaya çalışın; eşiği aşan koleksiyon kompakt biçimden çıkar ve belirgin biçimde daha fazla yer kaplar.
Kapanış#
Redis bellek yönetiminin özü şu birkaç karara iniyor: maxmemory değerini mutlaka ayarlayın ve kalıcılık açıksa RAM'in yarısında tutun, önbellek kullanımında politikayı allkeys-lru (veya allkeys-lfu) yapın, volatile-* seçtiyseniz anahtarlarınızın gerçekten TTL taşıdığını doğrulayın ve evicted_keys ile mem_fragmentation_ratio metriklerini izlemenize ekleyin. Üretimde KEYS * çalıştırmayın, dev anahtarları parçalayın ve silme işlemlerinde UNLINK tercih edin.
Bellek, Redis iş yükünde en doğrudan maliyet kalemidir; ihtiyacınız büyüdükçe kolayca yükseltebileceğiniz bir plan seçmek uzun vadede iş görür. Belleği esnek büyütülebilen bulut sunucu ve yüksek RAM seçenekli VDS paketlerimiz Redis için uygun bir taban sunar; tek makinenin belleği yetmemeye başladığında Redis Cluster kurulumu rehberindeki yatay ölçekleme yolunu değerlendirebilirsiniz. Kurulum, ayar ve bellek izlemesini devretmek isterseniz sunucu yönetimi hizmetimiz bu işi üstlenir.