Production ortamında Redis kullanıyorsanız ve “OOM command not allowed” hatası aldıysanız, bu yazı tam size göre. Redis’in bellek yönetimi, doğru yapılandırılmadığında sessiz sedasız sisteminizi çökertebilir. Ben de bu acıyı yaşadım, gece 2’de uyandım ve “maxmemory” kelimesini bir daha hiç unutmadım.
Redis Bellek Yönetimi Neden Bu Kadar Önemli?
Redis, tüm verisini RAM’de tutar. Bu onun hızlı olmasının temel sebebi, ama aynı zamanda dikkatli yönetilmesi gereken kritik bir özellik. Bellek dolunca Redis ne yapacağını bilmiyorsa, ya yeni yazma isteklerini reddeder ya da rastgele anahtarları siler. İkisi de production’da felakete yol açabilir.
Bir e-ticaret sisteminde oturum verilerini Redis’te sakladığınızı düşünün. Kampanya döneminde trafik patlarsa ve Redis bellek sınırına ulaşırsa, yanlış yapılandırılmış bir eviction policy ile müşteri oturumları uçup gidebilir. Ya da tam tersi, yeni oturumlar oluşturulamaz ve kullanıcılar giriş yapamaz. Her iki senaryo da korkunç.
Bu yüzden Redis bellek yönetimini anlamak, sadece bir optimizasyon meselesi değil, sistem güvenilirliği meselesi.
Redis Bellek Kullanımını Anlamak
Önce mevcut durumu görmek lazım. Redis’e bağlanıp bellek hakkında bilgi almak için:
redis-cli info memory
Bu komut size şu gibi bir çıktı verir:
# Memory
used_memory:1073741824
used_memory_human:1.00G
used_memory_rss:1207959552
used_memory_rss_human:1.15G
used_memory_peak:1342177280
used_memory_peak_human:1.25G
mem_fragmentation_ratio:1.12
maxmemory:2147483648
maxmemory_human:2.00G
maxmemory_policy:allkeys-lru
Buradaki kritik değerleri açıklayalım:
- used_memory: Redis’in fiilen kullandığı bellek miktarı
- used_memory_rss: İşletim sisteminin Redis’e ayırdığı gerçek RAM (genellikle daha yüksek)
- used_memory_peak: Şimdiye kadar ulaşılan en yüksek bellek kullanımı
- mem_fragmentation_ratio: 1.5’in üzerindeyse ciddi fragmentation var demektir, 1.0’ın altındaysa swap kullanılıyor olabilir
- maxmemory: Ayarladığınız maksimum bellek sınırı
- maxmemory_policy: Bellek dolunca ne yapılacağı
Anlık bellek durumunu izlemek için daha sık kullanabileceğiniz pratik bir komut:
redis-cli info memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation_ratio|maxmemory_policy"
maxmemory Nasıl Ayarlanır?
maxmemory parametresi, Redis’in kullanabileceği maksimum RAM miktarını belirler. Bu değeri ya redis.conf dosyasında kalıcı olarak, ya da runtime’da geçici olarak ayarlayabilirsiniz.
redis.conf üzerinden kalıcı ayar:
# /etc/redis/redis.conf veya /etc/redis.conf dosyasını düzenleyin
sudo nano /etc/redis/redis.conf
# Bu satırı bulun veya ekleyin:
maxmemory 2gb
# Diğer format örnekleri:
# maxmemory 512mb
# maxmemory 1073741824 (byte cinsinden 1GB)
# maxmemory 0 (sınırsız - önerilmez!)
Değişikliği uygulamak için:
sudo systemctl restart redis
# veya graceful reload için:
sudo systemctl reload redis
Runtime’da geçici ayar (restart gerekmez):
redis-cli config set maxmemory 2gb
# Doğrulama:
redis-cli config get maxmemory
Runtime’da yaptığınız değişiklik, Redis yeniden başlatılırsa sıfırlanır. Production’da her iki yerde de (hem conf hem runtime) ayarlamak en güvenlisi.
maxmemory için önerilen değerler:
- Sadece önbellek olarak kullanılıyorsa: Sunucu RAM’inin %75-80’i
- Oturum verisi de tutuluyorsa: Sunucu RAM’inin %60-70’i
- Persistent data varsa: Sunucu RAM’inin %50’si (AOF/RDB için yer bırakın)
- Konteyner ortamında: Konteyner limitinin %85’i
Eviction Policy Nedir ve Neden Önemlidir?
Bellek sınırına ulaşıldığında Redis’in ne yapacağını maxmemory-policy belirler. Yanlış policy seçmek, “bellek doldu, uygulama çöktü” yerine “bellek doldu, yanlış veri silindi, veri tutarsızlığı oluştu” gibi daha sinsi sorunlara yol açabilir.
Mevcut policy seçeneklerini inceleyelim:
- noeviction: Bellek dolduğunda hiçbir anahtar silmez, yeni yazma isteklerini hata döndürerek reddeder. Varsayılan değer budur. Veri kaybetmek istemiyorsanız ama hata yönetimini kendiniz yapacaksanız bu seçeneği kullanın.
- allkeys-lru: Tüm anahtarlar arasından en uzun süredir kullanılmayanları siler (Least Recently Used). Önbellek senaryolarında en yaygın tercih.
- allkeys-lfu: Tüm anahtarlar arasından en az kullanılanları siler (Least Frequently Used). Redis 4.0+ ile geldi, uzun vadede daha akıllı bir seçim.
- allkeys-random: Tüm anahtarlar arasından rastgele siler. Hangi verinin daha değerli olduğu belirsizse kullanılabilir ama genellikle önerilmez.
- volatile-lru: Sadece TTL (expire) ayarlanmış anahtarlar arasından LRU uygular. TTL’siz anahtarlar asla silinmez.
- volatile-lfu: Sadece TTL ayarlanmış anahtarlar arasından LFU uygular.
- volatile-random: Sadece TTL ayarlanmış anahtarlar arasından rastgele siler.
- volatile-ttl: TTL’i en kısa olan anahtarları önce siler, mantıklı bir sıralama sunar.
Policy ayarlama:
# redis.conf içinde:
maxmemory-policy allkeys-lru
# Runtime'da:
redis-cli config set maxmemory-policy allkeys-lru
# Mevcut policy'yi görme:
redis-cli config get maxmemory-policy
Hangi Senaryoda Hangi Policy?
Bu sorunun cevabı, Redis’i ne için kullandığınıza bağlı.
Senaryo 1: Saf önbellek (cache-only)
Tüm anahtarların eşit değerde olduğu, herhangi birinin silinmesinin kabul edilebilir olduğu durum. Uygulama cache miss’i tolere ediyor, gerektiğinde arkadaki veritabanından tekrar yükleyebiliyor.
maxmemory-policy allkeys-lru
# veya daha akıllı:
maxmemory-policy allkeys-lfu
allkeys-lfu, sık erişilen “hot” anahtarları korurken seyrek kullanılanları siler. Özellikle Pareto dağılımı olan (az sayıda anahtar, trafiğin büyük bölümünü oluşturuyor) veri setlerinde çok başarılı.
Senaryo 2: Karışık kullanım (kalıcı + geçici veriler)
Bazı anahtarlar kritik (kullanıcı oturumları, sipariş durumları) ve silinmemeli. Bazı anahtarlar ise geçici önbellek verisi.
maxmemory-policy volatile-lru
Kritik verilerinize TTL koymayın, önbellek verilerinize TTL koyun. Bellek dolduğunda Redis sadece TTL’li olanları siler.
# Kritik veri - TTL yok
redis-cli set user:session:12345 "session_data"
# Önbellek verisi - TTL var
redis-cli setex product:cache:789 3600 "product_data"
Senaryo 3: Job queue veya pub/sub
Redis’i mesaj kuyruğu olarak kullanıyorsanız veri kaybı kabul edilemez.
maxmemory-policy noeviction
Bu durumda uygulamanızın “ENOMEM” hatasını yakalayıp uygun şekilde handle etmesi gerekir.
Senaryo 4: Rate limiting
TTL’li sayaçlarla rate limiting yapıyorsanız:
maxmemory-policy volatile-ttl
TTL’i yakında dolacak olan rate limit sayaçları önce silinir, bu oldukça mantıklı bir davranış.
LRU ve LFU Parametrelerini İnce Ayar Yapmak
Redis, LRU/LFU hesaplamalarını tam olarak değil, örnekleme yöntemiyle yapar. Bu, bellekten tasarruf sağlar ama hassasiyeti azaltır.
# LRU örnekleme sayısını ayarlama (varsayılan: 5)
# Daha yüksek değer = daha doğru LRU ama daha fazla CPU kullanımı
maxmemory-samples 10
# Redis.conf'ta:
# maxmemory-samples 5
LFU için ek parametreler:
# LFU decay faktörü: Kullanım sayacının ne kadar hızla azalacağı
# Dakika cinsinden, varsayılan: 1
lfu-decay-time 1
# LFU başlangıç değeri: Yeni eklenen anahtarların başlangıç sayacı
# Varsayılan: 5, 0-255 arası
lfu-log-factor 10
lfu-log-factor değerini artırmak, çok sık erişilen anahtarları diğerlerinden daha belirgin biçimde ayırt etmenizi sağlar. 10 milyondan fazla istek gören anahtarlarla 1000 istek gören anahtarları aynı kefeye koymak istemiyorsanız bu değeri artırın.
Bellek Optimizasyon Teknikleri
Policy ayarlamak yeterli değil, Redis’in belleği verimli kullanması için yapı düzeyinde optimizasyonlar da önemli.
Veri yapısı seçimi:
# Kötü: Her alan için ayrı string anahtarı
redis-cli set user:1:name "Ali"
redis-cli set user:1:email "[email protected]"
redis-cli set user:1:age "30"
# İyi: Hash kullanımı (çok daha az bellek)
redis-cli hset user:1 name "Ali" email "[email protected]" age "30"
Hash’ler, küçük boyutlarda ziplist encoding kullanır ve string’lere göre belleği çok daha verimli kullanır.
Hash ve List threshold ayarları:
# Hash için ziplist encoding sınırları
hash-max-ziplist-entries 128
hash-max-ziplist-value 64
# List için quicklist encoding
list-max-ziplist-size -2 # Her quicklist node'u max 8kb
# Sorted set için
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
Bu eşik değerlerin altında kalan veri yapıları daha kompakt encoding kullanır. Ortalama bir hash için bu ayarlar doğru tutulduğunda bellek kullanımını %40-60 oranında azaltabilirsiniz.
Key expiry kontrolü:
# Aktif expire işlemi yoğunluğu (varsayılan: 1, max: 10)
# Yüksek değer CPU kullanır ama belleği daha hızlı temizler
hz 10
# Lazy freeing - büyük anahtarları arka planda sil
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
lazyfree-lazy-eviction yes özellikle büyük veri yapılarını (büyük hash’ler, setler) silerken gecikme sorunlarını önler. Silme işlemi ana thread’i bloklamaz.
Bellek Kullanımını İzleme ve Uyarı Kurma
Bellek yönetimi sadece ayar yapmak değil, sürekli izlemek demek.
#!/bin/bash
# /usr/local/bin/redis-memory-check.sh
REDIS_CLI="redis-cli"
THRESHOLD=80 # %80 dolulukta uyar
USED=$(redis-cli info memory | grep "used_memory:" | awk -F: '{print $2}' | tr -d 'r')
MAX=$(redis-cli config get maxmemory | tail -1)
if [ "$MAX" -eq "0" ]; then
echo "WARNING: maxmemory ayarlanmamis, Redis sinırsız bellek kullanabilir!"
exit 1
fi
PERCENT=$((USED * 100 / MAX))
echo "Redis bellek kullanimi: %${PERCENT} (${USED} / ${MAX} byte)"
if [ "$PERCENT" -ge "$THRESHOLD" ]; then
echo "UYARI: Redis bellek kullanimi %${PERCENT} esigini asti!"
# Buraya mail/Slack bildirimi ekleyebilirsiniz
exit 2
fi
exit 0
chmod +x /usr/local/bin/redis-memory-check.sh
# Cron ile her 5 dakikada bir kontrol:
echo "*/5 * * * * root /usr/local/bin/redis-memory-check.sh >> /var/log/redis-memory.log 2>&1" >> /etc/crontab
Prometheus ve Grafana kullananlar için redis_exporter metrikleri:
# redis_exporter kurulumu (Debian/Ubuntu)
wget https://github.com/oliver006/redis_exporter/releases/download/v1.55.0/redis_exporter-v1.55.0.linux-amd64.tar.gz
tar xzf redis_exporter-v1.55.0.linux-amd64.tar.gz
sudo mv redis_exporter /usr/local/bin/
# Servis olarak başlatma
sudo tee /etc/systemd/system/redis_exporter.service << EOF
[Unit]
Description=Redis Exporter
After=network.target
[Service]
User=redis
ExecStart=/usr/local/bin/redis_exporter --redis.addr=redis://localhost:6379
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable --now redis_exporter
Önemli Prometheus metrikleri:
- redis_memory_used_bytes: Kullanılan bellek
- redis_memory_max_bytes: Maksimum bellek limiti
- redis_evicted_keys_total: Toplam evicted anahtar sayısı (bu artıyorsa dikkat!)
- redis_expired_keys_total: TTL süresi dolup silinen anahtarlar
- redis_mem_fragmentation_ratio: Fragmentation oranı
Gerçek Dünya: Bir Production Vakası
Geçen yıl, yoğun kullanılan bir içerik platformunda Redis’i optimize etmek durumunda kaldım. Bellek kullanımı gece aniden spike yapıyor, allkeys-random policy nedeniyle tamamen rastgele veriler siliniyordu. Sabah uyanıldığında bazı sayfalar cache miss yüzünden veritabanını boğuyordu.
Analiz yaptım:
# Hangi key pattern'lar en çok yer kaplıyor?
redis-cli --bigkeys
# Veya daha detaylı analiz için:
redis-cli --scan --pattern "*" | head -1000 | while read key; do
redis-cli debug object "$key" 2>/dev/null | grep -oP 'serializedlength:K[0-9]+'
done | awk '{sum+=$1} END {print "Toplam: " sum " byte"}'
Sonuçta iki sorun çıktı: Birincisi, büyük JSON stringler hash yapısı yerine düz string olarak saklanıyordu. İkincisi, bir crawler servisi rastgele scrape edilmiş içerikleri TTL’siz olarak cache’e yazıyordu ve bunlar birikiyordu.
Çözüm:
# Policy değişikliği:
redis-cli config set maxmemory-policy volatile-lfu
# Tüm önbellek yazımlarına TTL zorunlu hale getirildi (uygulama tarafı)
# Crawler servisine max 1 saatlik TTL zorunluluğu getirildi
# Hash encoding'e geçildi
# Sonuç: Bellek kullanımı %40 azaldı, eviction sıfıra düştü
Özel Durumlar ve Dikkat Edilmesi Gerekenler
Redis Cluster’da eviction:
Her node kendi maxmemory limitini bağımsız olarak yönetir. Cluster genelinde bir limit yoktur. Her node için ayrı ayrı ayar yapmanız gerekir.
# Tüm cluster node'larını kontrol:
redis-cli --cluster info localhost:7000
Replica’larda eviction:
Varsayılan olarak replica’lar eviction yapmaz. Master evict ettiğinde replica’ya DEL komutu gönderilir. Bu davranışı değiştirmek genellikle önerilmez.
OOM Killer ve Linux:
Redis’in Linux OOM Killer tarafından öldürülmesini engellemek için:
# Redis process'inin OOM score'unu düşür
echo -17 > /proc/$(pgrep redis-server)/oom_score_adj
# Kalıcı hale getirmek için redis.service'e ekleyin:
# OOMScoreAdjust=-17
Swap kullanımı kontrolü:
Redis swap kullanmaya başlarsa performans felç olur:
# Redis'in swap kullanıp kullanmadığını kontrol:
cat /proc/$(pgrep redis-server)/status | grep VmSwap
# Sistem genelinde swap kontrolü:
free -h
vmstat 1 5
Redis için swap’ı tamamen devre dışı bırakmak genellikle önerilir, çünkü swap kullanan Redis neredeyse hiç kullanılmayan Redis demektir.
Sonuç
Redis bellek yönetimi üç temel bileşenden oluşuyor: maxmemory ile doğru sınır belirlemek, maxmemory-policy ile akıllı eviction stratejisi seçmek ve veri yapılarını verimli kullanmak.
Başlangıç noktası olarak şunu öneriyorum: Saf önbellek kullanımı için allkeys-lfu (Redis 4.0+) mükemmel bir seçenek. Karışık kullanım için volatile-lru ve kritik verilere TTL koymama stratejisi çok işe yarıyor. Asla noeviction ile bırakıp uygulamanızın hata yönetimini yapmadığınız durumda kalmayın.
Bellek izlemeyi mutlaka otomatize edin. Gece 2’de alarm almak can sıkıcı, ama sabah kalkıp sistemin çökmüş olduğunu görmekten iyidir. redis_evicted_keys_total metriği artmaya başladığında sorunun önüne geçmeniz için genellikle hala zamanınız var.
Son olarak: maxmemory değerini asla 0’da bırakmayın. “Sınırsız bellek” diye bir şey yok, sadece “henüz tükenmemiş bellek” var.