Cache Provider'lar: Caffeine, Redis
Spring Cache abstraction, farklı cache provider'lar üzerine çalışır. Doğru provider seçimi; uygulamanızın mimarisine, ölçeğine ve performans gereksinimlerine bağlıdır. Bu derste her provider'ı detaylı inceleyecek, konfigürasyon seçeneklerini, eviction politikalarını ve monitoring yaklaşımlarını öğreneceğiz.
ConcurrentMapCacheManager (Default)
// application.properties — hiçbir şey yapmazsanız bu kullanılır
spring.cache.type=simpleBasit ConcurrentHashMap tabanlıdır. Tek JVM için çalışır.
Neden production'da kullanılmaz:
TTL desteği yok — entry'ler sonsuza kadar kalır (memory leak!)
Eviction yok — bellek doluncaya kadar büyür
İstatistik yok — hit/miss oranı bilinmez
Size limit yok — bellek kontrolü yapılamaz
Sadece development ve unit test için uygundur.
Caffeine — Yüksek Performanslı Local Cache
Caffeine, Java dünyasının en hızlı local cache kütüphanesidir. Google Guava Cache'in yazarı tarafından geliştirilmiş, near-optimal hit rate sağlayan W-TinyLFU eviction algoritması kullanır.
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>Temel Konfigürasyon — application.properties ile:
spring.cache.type=caffeine
spring.cache.caffeine.spec=maximumSize=10000,expireAfterWrite=30m,recordStatsDetaylı Konfigürasyon — Java Config ile:
@Configuration
@EnableCaching
public class CaffeineCacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.maximumSize(10_000) // Max entry sayısı
.expireAfterWrite(Duration.ofMinutes(30)) // Yazma sonrası expire
.expireAfterAccess(Duration.ofMinutes(10)) // Son erişimden sonra expire
.recordStats()); // İstatistik toplama
// Cache name'leri önceden tanımla (isteğe bağlı)
manager.setCacheNames(List.of("products", "users", "categories"));
manager.setAllowNullValues(false); // null cache'lemeyi engelle
return manager;
}
}Caffeine Konfigürasyon Parametreleri:
| Parametre | Açıklama | Örnek |
|---|---|---|
maximumSize | Maksimum entry sayısı | maximumSize(10_000) |
maximumWeight | Ağırlık bazlı limit (size yerine) | maximumWeight(100_MB) |
expireAfterWrite | Yazma sonrası expire | expireAfterWrite(30m) |
expireAfterAccess | Son erişimden sonra expire | expireAfterAccess(10m) |
refreshAfterWrite | Arka planda yenileme | refreshAfterWrite(15m) |
recordStats | Hit/miss istatistikleri | recordStats() |
weakKeys | Key'ler GC'ye tabi | weakKeys() |
softValues | Value'lar soft reference | softValues() |
⚠️ Dikkat:
expireAfterWriteveexpireAfterAccessbirlikte kullanıldığında hangisi önce dolarsa o geçerlidir.
Cache Bazında Farklı Konfigürasyon:
@Configuration
@EnableCaching
public class MultiCaffeineCacheConfig {
@Bean
public CacheManager cacheManager() {
// Her cache için farklı Caffeine instance
SimpleCacheManager manager = new SimpleCacheManager();
manager.setCaches(List.of(
buildCache("products", 10_000, Duration.ofHours(2)),
buildCache("users", 5_000, Duration.ofMinutes(15)),
buildCache("categories", 500, Duration.ofHours(24)),
buildCache("search", 20_000, Duration.ofMinutes(5))
));
return manager;
}
private CaffeineCache buildCache(String name, long maxSize, Duration ttl) {
return new CaffeineCache(name,
Caffeine.newBuilder()
.maximumSize(maxSize)
.expireAfterWrite(ttl)
.recordStats()
.build());
}
}Caffeine Eviction Politikaları
W-TinyLFU (Window Tiny Least Frequently Used):
Caffeine'in varsayılan eviction algoritmasıdır. LRU ve LFU'nun en iyi özelliklerini birleştirir:
Yeni entry'ler → Window Cache (LRU, %1)
↓
Admission Filter (TinyLFU)
↓
Main Cache (Segmented LRU, %99)Window Cache (%1): Yeni entry'ler buraya girer (LRU)
Admission Filter: TinyLFU count-min sketch ile frekans tahmin eder
Main Cache (%99): Sık erişilen entry'ler burada kalır
Bu hibrit yaklaşım, hem recency hem frequency'yi dikkate alarak near-optimal hit rate sağlar.
Karşılaştırma:
| Politika | Hit Rate | Scan Resistance | Burst Resistance |
|---|---|---|---|
| LRU | Orta | Kötü | İyi |
| LFU | İyi | İyi | Kötü |
| W-TinyLFU | En İyi | İyi | İyi |
Scan resistance: Bir kerelik büyük veri taramasının (DB full scan gibi) cache'i "kirletmemesi".
EhCache 3 — Enterprise Cache
EhCache 3, tiered storage desteği ile hem heap hem off-heap hem disk kullanabilir:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>@Configuration
@EnableCaching
public class EhCacheConfig {
@Bean
public CacheManager cacheManager() {
org.ehcache.CacheManager ehCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("products",
CacheConfigurationBuilder
.newCacheConfigurationBuilder(Long.class, Product.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(1000, EntryUnit.ENTRIES) // 1000 entry heap'te
.offheap(100, MemoryUnit.MB) // 100MB off-heap
.disk(1, MemoryUnit.GB, true)) // 1GB disk (persistent)
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(
Duration.ofMinutes(30))))
.build(true);
return new JCacheCacheManager(/* JCache wrapper */);
}
}Caffeine vs EhCache 3 Karşılaştırma
| Özellik | Caffeine | EhCache 3 |
|---|---|---|
| Hız | Çok hızlı (en hızlı JVM cache) | Hızlı ama Caffeine'den yavaş |
| Eviction | W-TinyLFU (near-optimal) | LRU, LFU, FIFO |
| Storage | Sadece heap | Heap + Off-heap + Disk |
| Persistence | Yok (restart = veri kaybı) | Disk persistence var |
| Distributed | Yok (sadece local) | Terracotta ile cluster |
| Konfigürasyon | Java API / properties | XML, Java API |
| Dependency | Tek JAR (~1MB) | Birden fazla JAR |
| Monitoring | CacheStats API | JMX, Micrometer |
| Kullanım | Web app, microservice | Enterprise, büyük veri setleri |
Tavsiye: Çoğu Spring Boot uygulaması için Caffeine yeterlidir. EhCache 3, ancak off-heap/disk storage veya persistence gerekiyorsa tercih edin.
Cache Metrics ve Monitoring
Caffeine Stats:
@RestController
@RequiredArgsConstructor
public class CacheMetricsController {
private final CacheManager cacheManager;
@GetMapping("/admin/cache/stats")
public Map<String, Object> getCacheStats() {
Map<String, Object> stats = new LinkedHashMap<>();
cacheManager.getCacheNames().forEach(name -> {
Cache cache = cacheManager.getCache(name);
if (cache instanceof CaffeineCache caffeineCache) {
com.github.benmanes.caffeine.cache.stats.CacheStats s =
caffeineCache.getNativeCache().stats();
stats.put(name, Map.of(
"hitCount", s.hitCount(),
"missCount", s.missCount(),
"hitRate", String.format("%.2f%%", s.hitRate() * 100),
"evictionCount", s.evictionCount(),
"estimatedSize", caffeineCache.getNativeCache().estimatedSize()
));
}
});
return stats;
}
}Micrometer ile Spring Boot Actuator entegrasyonu:
# application.properties
management.endpoints.web.exposure.include=caches,metrics// Caffeine recordStats() aktifse, Spring Boot otomatik olarak
// aşağıdaki metric'leri Micrometer'a expose eder:
// cache.gets{result=hit}
// cache.gets{result=miss}
// cache.evictions
// cache.size
// Prometheus'tan sorgulama:
// rate(cache_gets_total{cache="products",result="hit"}[5m])
// /
// rate(cache_gets_total{cache="products"}[5m])
// = hit rateProvider Seçim Rehberi
| Senaryo | Önerilen Provider | Gerekçe |
|---|---|---|
| Development / Test | ConcurrentMap | Sıfır konfigürasyon |
| Single instance, web app | Caffeine | En hızlı, basit |
| Single instance, büyük veri | EhCache 3 | Off-heap + disk |
| Microservice, multi-instance | Redis | Distributed, paylaşımlı |
| Microservice + local | Caffeine (L1) + Redis (L2) | Multi-level |
Yaygın Hatalar
❌ Production'da ConcurrentMap kullanmak: TTL ve eviction olmadığı için memory leak oluşur.
❌ maximumSize koymamak: Cache sonsuza kadar büyür, OOM riski.
❌ recordStats() aktif etmemek: Hit rate bilinmeden cache tuning yapılamaz.
❌ Tüm cache'lere aynı TTL vermek: Sık değişen veri (kullanıcı sepeti) ile nadir değişen veri (kategori listesi) farklı TTL ister.
💡 Özet: Caffeine, single-instance için en iyi seçimdir (W-TinyLFU, en yüksek hit rate). EhCache 3, off-heap ve disk persistence gerektiğinde tercih edilir. Redis, distributed cache için zorunludur. Her zaman
recordStats()aktif edin ve hit rate'i monitoring edin.
AI Asistan
Sorularını yanıtlamaya hazır