Caching Stratejileri: Yerel Bellekten Distributed Sistemlere
In-memory uygulama cache'lerinden distributed Redis cluster'lara ve CDN edge caching'e kadar çok katmanlı caching stratejilerini uygulamaya yönelik kapsamlı bir rehber. Cache-aside ve write-through pattern'leri ne zaman kullanılır, ElastiCache ile MemoryDB arasında nasıl seçim yapılır ve production'da cache stampede nasıl önlenir öğrenin.
Caching basit görünüyor, ta ki %15 hit rate'e bakıp pahalı Redis cluster'ının neden yardımcı olmadığını merak edene kadar. Ya da daha kötüsü, popüler bir cache key expire olduğunda ve 5.000 eşzamanlı request onu yeniden oluşturmak için database'e hücum ettiğinde database'in çöktüğünü izlemek.
Etkili caching'in sadece Redis ekleyip işi bitirmek olmadığını öğrendim. In-memory uygulama cache'lerinden distributed sistemlere ve CDN edge caching'e kadar tam hiyerarşiyi anlamak ve hangi pattern'in hangi problemi çözdüğünü bilmek gerekiyor.
Bu rehber karşılaştığım teknik kararları kapsıyor: cache-aside'ın write-through'a karşı ne zaman mantıklı olduğu, AWS ElastiCache ile MemoryDB arasında nasıl seçim yapılır (ipucu: birbirinin yerine kullanılamazlar), distributed cache scaling için consistent hashing implementasyonu ve cache stampede'i database'i çökertmeden önce nasıl önlersin.
Cache Pattern'lerini Anlamak
Cache pattern'leri sadece akademik konseptler değil. Cache-aside ile write-through arasındaki fark, stale data şikayetleri mi yoksa yavaş write performance mı alacağını belirleyebilir. Her pattern'in production'da gerçekte ne yaptığını görelim.
Cache-Aside (Lazy Loading)
Uygulama hem cache'i hem database'i doğrudan yönetir. Okumada önce cache'i kontrol et. Miss durumunda database'den çek ve cache'i doldur. En yaygın pattern bu çünkü basit ve verimli.
Cache-aside ne zaman kullanılır:
- Tüm datanın sık erişilmediği read-heavy workload'lar
- Hafif staleness tolere edilebilen data
- Sadece gerçekten kullanılan şeyleri cache'lemek istiyorsan
Trade-off'lar:
- İlk request cache miss latency yaşar
- Popüler expire olan key'lerde cache stampede riski (bunu düzelteceğiz)
- Verimli memory kullanımı çünkü sadece erişilen data cache'lenir
Write-Through Pattern
Her yazma hem cache'e hem database'e gider. Cache database ile senkronize kalır ve okuyucular her zaman cache'den fresh data alır.
Write-through ne zaman kullanılır:
- Cache ve database arasında strong consistency gereklilikleri
- Write operasyonları sık
- Read-heavy workload'lar her zaman fresh cache'den faydalanır
Trade-off'lar:
- Write latency artar (hem cache hem database güncellemeli)
- Hiç okunmayacak data'yı cache'ler
- Daha yüksek cache hit rate'leri çünkü cache her zaman dolu
Write-Behind (Write-Back) Pattern
Yazmalar hemen cache'e gider, sonra asenkron olarak database'e yazılır. Mükemmel write performance sağlar ama karmaşıklık ve potansiyel data loss riski getirir.
Write-behind ne zaman kullanılır:
- Write-heavy workload'lar (analytics, log'lar, metrikler)
- Cache failure durumunda potansiyel data loss tolere edilebilir
- Database write performance bottleneck
Trade-off'lar:
- Cache persistence öncesi fail olursa data loss riski
- Daha karmaşık implementasyon ve monitoring
- Batching sayesinde mükemmel write performance
Cache Stampede'i Önlemek
Cache stampede (thundering herd) popüler bir cache key expire olduğunda ve yüzlerce ya da binlerce request eşzamanlı onu yeniden oluşturmaya çalıştığında olur. Database connection pool'un tükenir ve her şey cascade eder.
Nasıl önlenir:
Probabilistic Early Expiration
Cache expire olmasını beklemek yerine, kalan TTL'e göre probabilistik olarak expiration'dan önce refresh et. Bu refresh yükünü yayar.
Distributed Locking
Cache miss olduğunda, data'yı kimin yeniden oluşturacağını koordine etmek için Redis kullan. Diğer request'ler kısaca bekler ve retry yapar.
Request Coalescing
Aynı in-flight request'leri uygulama seviyesinde deduplicate et. Aynı cache key için 100 request gelirse, sadece biri gerçekten data çeker.
AWS Caching Servisleri: Ne Zaman Hangisi
AWS ElastiCache, MemoryDB ve DAX sunuyor. Birbirlerinin yerine kullanılamazlar - her biri farklı use case'lere hizmet eder.
ElastiCache for Redis
En iyisi:
- Birden fazla uygulama sunucusu arasında session management
- Genel amaçlı caching katmanı (cache-aside pattern)
- Pub/sub messaging pattern'leri
- Leaderboard'lar, rate limiting, real-time analytics
Teknik özellikler:
- Latency: Sub-milisaniye
- Persistence: Opsiyonel snapshot'lar (real-time değil)
- Consistency: Eventual
- Fiyatlandırma: cache.r6g.large için ~150/ay
MemoryDB for Redis
En iyisi:
- Microservice'ler için primary database (sadece cache değil)
- Durability gerektiren real-time analytics
- Redis hızı + ACID garantileri gereken mission-critical uygulamalar
- Finansal transaction'lar, inventory management
Teknik özellikler:
- Latency: Sub-milisaniye read'ler, tek haneli milisaniye write'lar
- Persistence: Transaction log ile tam durable persistence
- Consistency: Strong (senkron replication)
- Multi-AZ: Sıfır data loss ile otomatik failover
- Fiyatlandırma: db.r6g.large için ~293/ay (ElastiCache'in 1.5x'i)
MemoryDB'yi ElastiCache yerine ne zaman seçmeli:
- Redis'i primary database olarak kullanman gerekiyor (sadece cache değil)
- Hiç data loss tolere edemezsin
- Strong consistency garantileri gerekli
- Ayrı database + cache mimarisini ortadan kaldırmak istiyorsun
DynamoDB Accelerator (DAX)
En iyisi:
- Sadece DynamoDB'ye özel hızlandırma
- Read-heavy DynamoDB workload'ları (gaming leaderboard'ları)
- Eventually consistent read'ler kabul edilebilir
- Scale'de mikrosaniye latency gerekli
Teknik özellikler:
- Latency: Cache'li read'ler için mikrosaniye
- Entegrasyon: Native DynamoDB API uyumluluğu
- Consistency: Sadece eventually consistent read'ler
- Fiyatlandırma: dax.r4.large için ~$0.40/saat
Önemli sınırlamalar:
- Sadece DynamoDB ile çalışır (genel amaçlı değil)
- Query/scan cache'i get/batch-get cache'inden ayrı
- Strongly consistent read desteği yok
- Conditional update'leri cache'leyemez
Karar Matrisi
Distributed Cache'ler için Consistent Hashing
Birden fazla cache node'un varsa, hangi node'un hangi key'i sakladığına nasıl karar verirsin? Basit modulo hashing (hash(key) % N) node'lar değiştiğinde büyük redistribution'a neden olur:
- Server ekle: ~%50 key taşınır
- Server kaldır: ~%50 key taşınır
Consistent hashing redistribution'ı ~1/N key'e minimize eder.
İmplementasyon
Virtual Node'lar Neden Önemli
Virtual node'lar olmadan basit consistent hashing dengesiz dağılım oluşturabilir. Virtual node'lar (vnode'lar) bunu çözer:
- Her fiziksel node ring üzerinde dağılmış 100-200 virtual node alır
- Daha uniform data dağılımı
- Node ekleme/çıkarma sırasında daha smooth load balancing
- Server'ları kapasiteye göre ağırlıklandırabilirsin (daha fazla vnode = daha fazla data)
Multi-Tier Caching Mimarisi
Gerçek performance cache'leri stratejik olarak katmanlamaktan gelir. İşte pratik üç katmanlı bir mimari:
L1: In-Process Memory Cache
- Boyut: Instance başına 50-100 MB
- TTL: 30-60 saniye
- Amaç: Hot data için ultra-hızlı erişim
- Teknoloji: LRU cache
L2: Distributed Redis Cache
- Boyut: 10-100 GB cluster
- TTL: 5-60 dakika
- Amaç: Instance'lar arası paylaşımlı cache
- Teknoloji: ElastiCache Redis cluster
L3: CDN Edge Cache
- Boyut: Sınırsız (CloudFront)
- TTL: 1 saat - 1 yıl
- Amaç: Global edge dağıtımı
- Teknoloji: CloudFront
İmplementasyon
CloudFront Caching Stratejileri
CDN caching uygulama caching'den farklı. İçeriği global olarak uzun TTL'lerle dağıtıyorsun, bu da invalidation stratejisinin önemli olduğu anlamına gelir.
Cache Behavior Configuration
Farklı içerik türleri farklı cache policy'leri gerektirir:
Invalidation Stratejisi
CloudFront invalidation maliyetleri toplanır (ilk 1.000/ay'dan sonra path başına $0.005). Bunun yerine versiyonlu URL'ler kullan:
React Query ile Client-Side Caching
Frontend caching genellikle gözden kaçırılır ama kullanıcı deneyimi için kritik. React Query (TanStack Query) stale-while-revalidate pattern ile sofistike client-side caching sağlar.
Daha İyi UX için Prefetching
Kullanıcılar ihtiyaç duymadan önce data'yı prefetch et, anında navigasyon için:
Cache Monitoring ve Optimization
Ölçmediğin şeyi optimize edemezsin. İşte kritik metrikler:
Ana Metrikler
1. Hit Rate
Hedef: Workload'a göre %85-95
- %80'in altında: Cache key tasarımını, TTL ayarlarını incele
- Formül:
(hit'ler / (hit'ler + miss'ler)) * 100
2. Latency Percentile'ları
- P50: Redis için ~1-2ms
- P99: <10ms olmalı
- P99.9: >50ms ise alert
3. Memory Kullanımı
- Hedef: %70-80 kullanım
- Alert: >%90 (eviction riski)
4. Eviction Rate
- Yüksek eviction = daha fazla memory ya da daha kısa TTL'ler gerekli
Monitoring İmplementasyonu
Yaygın Hatalar ve Dersler
1. Dynamic Data'yı Aşırı Cache'lemek
User'a özel data'yı uzun TTL ile cache'lemek kullanıcıların stale data görmesine ve destek bilet sayısının artmasına yol açar.
Çözüm: Data'yı volatility'sine göre sınıflandır:
2. Kötü Cache Key Tasarımı
Cache key'lerine timestamp ya da random değerler eklemek hit rate'i yok eder.
3. Cache Failure'ları Görmezden Gelmek
Cache failure uygulamanı düşürmemeli. Her zaman fallback implement et:
4. CloudFront Invalidation İstismarı
Sık invalidation maliyetleri artırır. Bunun yerine versiyonlu URL'ler kullan:
Maliyet Optimizasyonu
AWS Servis Fiyatlandırması (us-east-1)
ElastiCache Redis (cache.r6g.large: 13.07 GB):
- On-Demand: 150/ay
- 3-node cluster: ~$450/ay
MemoryDB (db.r6g.large: 13.07 GB):
- On-Demand: 293/ay
- 3-node cluster: ~$879/ay (ElastiCache'in 1.5x'i)
CloudFront:
- İlk 10 TB/ay: $0.085/GB
- HTTP/HTTPS request'leri: 10.000 başına $0.0075
- Invalidation: İlk 1.000 path ücretsiz, sonrası path başına $0.005
Right-Sizing Stratejisi
Önemli Çıkarımlar
Birden fazla projede caching ile çalışmak şu pattern'leri öğretti:
1. Cache pattern'leri önemli: Read-heavy için cache-aside, consistency için write-through, write-heavy için write-behind. Gerçek workload'una göre seç.
2. Stampede'i erken önle: Problem yaşamadan önce distributed locking ve request coalescing implement et. Bir incident'tan sonra eklemek çok daha zor.
3. AWS servisleri birbirinin yerine kullanılamaz: Genel caching için ElastiCache, durability gerekiyorsa MemoryDB, sadece DynamoDB için DAX. İhtiyacın olmayan özellikler için fazla ödeme yapma.
4. Multi-tier caching çalışır: L1 in-memory + L2 Redis + L3 CDN maliyet başına en iyi performance'ı sağlar. Her katmanın bir amacı var.
5. Sürekli monitoring yap: Cache hit rate, latency, memory kullanımı ve request başına maliyet. Gerçek kullanıma göre aylık right-size yap.
6. Failure için tasarla: Cache performance'ı iyileştirmeli, single point of failure olmamalı. Her zaman graceful degradation implement et.
7. Invalidate etme, versiyonla: CloudFront invalidation maliyetleri toplanır. Versiyonlu asset'ler ücretsiz ve anında.
%15 hit rate ile %90 hit rate arasındaki fark genellikle sadece doğru cache key tasarımı ve TTL yönetimi. Temellerle başla, her şeyi monitoring yap ve gerçek metriklere göre optimize et.