asyncfunctionrenderPage(){const data =awaitgetPageDatas();return data;}
Bileşenler hazırlanıyor...
Not içeriği yükleniyor...
// Markdown dosyası okunuyor
// İçerik işleniyor
// Syntax highlighting hazırlanıyor
C# ile In-Memory ve Redis Distributed Caching | Kapsamlı Rehber | Kubilay Bozak
C# Kullanarak In-Memory Caching ve (Redis)Distributed Caching Uygulaması
2025-07-20
25 min
C# ile In-Memory ve Redis Distributed Caching implementasyonu. Monolit mimari, Onion Architecture ve Repository Pattern ile cache entegrasyonu, best practices ve performance optimization.
C#
Redis
Caching
MemoryCache
DistributedCache
Architecture
Repository Pattern
Performance
Modern .NET uygulamalarında performans optimizasyonu ve ölçeklenebilirlik açısından caching mekanizmaları kritik bir rol oynamaktadır. Bu rehberde C# ile hem In-Memory Caching hem de Redis Distributed Caching implementasyonlarını, farklı mimari yaklaşımlarında nasıl uygulanacağını ve best practice'leri detaylı örneklerle inceleyeceğiz. Başlangıç seviyesinden ileri seviyeye kadar her geliştirici için pratik bilgiler sunarak, gerçek projelerde uygulanabilir çözümler geliştireceğiz.
Cache sistemleri, özellikle yüksek trafikli uygulamalarda hem kullanıcı deneyimini iyileştirmek hem de sistem kaynaklarını verimli kullanmak için vazgeçilmez araçlardır. In-Memory caching, tek sunucu ortamlarında hızlı veri erişimi sağlarken, Redis gibi distributed cache çözümleri ölçeklenebilir mimarilerde tutarlı veri paylaşımını mümkün kılar. Bu rehberde her iki yaklaşımı da derinlemesine ele alacak, hangi durumda hangisinin tercih edilmesi gerektiğini örneklerle açıklayacağız.
🎯 Bu Rehberde Öğrenecekleriniz
Konu
Açıklama
In-Memory Caching
IMemoryCache ile temel cache implementasyonu
Redis Distributed Caching
IDistributedCache ile Redis entegrasyonu
Monolit Mimari Kurulumu
Tek uygulama içinde caching yapılandırması
Shared Klasör Yapısı
Ortak kullanım için cache servislerinin organize edilmesi
Onion Architecture
Temiz mimari prensipleri ile cache implementasyonu
Repository Design Pattern
Repository katmanında caching entegrasyonu
Best Practices
Performans, güvenlik ve bakım açısından en iyi yaklaşımlar
🚀 Temel Kurulumlar
Modern .NET uygulamalarında cache implementasyonu için öncelikle doğru NuGet paketlerinin kurulması ve temel konfigürasyonların yapılması gerekmektedir. Bu bölümde hem in-memory hem de distributed caching için gerekli paketleri ve yapılandırmaları detaylı olarak inceleyeceğiz.
📦 Gerekli NuGet Paketleri
.NET'in sunduğu cache abstraksiyonları sayesinde hem in-memory hem de distributed caching implementasyonları oldukça kolaydır. Microsoft.Extensions.Caching namespace'i altında yer alan interface'ler sayesinde cache türünü değiştirmek için minimum kod değişikliği gerekir.
In-Memory Caching için gerekli paketler:
Program.cs dosyasında cache servislerinin doğru şekilde yapılandırılması, uygulamanın performansını doğrudan etkiler. In-Memory cache için bellek limitleri, Redis cache için ise connection string ve instance ayarları kritik önem taşır.
bash
using Microsoft.Extensions.Caching.Memory;using Microsoft.Extensions.Caching.Distributed;using StackExchange.Redis;var builder = WebApplication.CreateBuilder(args);// In-Memory Cache Konfigürasyonu - TÜM OPTIONS
builder.Services.AddMemoryCache(options =>{ // TEMEL BOYUT YÖNETİMİ
options.SizeLimit =1024; // Cache boyut sınırı (entry sayısı) options.CompactionPercentage // İSTATİSTİK VE İZLEME
// Redis Distributed Cache Konfigürasyonu - TÜM OPTIONS
// TEMEL BAĞLANTI YÖNETİMİ
// GELİŞMİŞ KONFIGÜRASYON
// FACTORY YÖNETİMİ
// PROFILING VE İZLEME
🔧 Tüm Memory Cache Options Detayları
📊 Temel Boyut Yönetimi Options
MemoryCache Kavramı – Dolap Analojisiyle Anlayalım
Hayal et ki bir mutfağın var ve orada bir dolap var. Bu dolapta çeşitli yiyecekleri saklıyorsun: kavanozlar, tabaklar, kutular… Ama bu dolabın bir sınırı var değil mi? Yani dolabın bir kapasitesi sınırlı: Ne kadar büyük şey koyarsan, o kadar az şey sığar. Her şeyden azar azar koyarsan, daha fazla şey sığar.
🧠 Bu Ne Alaka? - Yazılımda da Bir Dolap Var
Yazılımda da bu dolap gibi çalışan bir yapı var:
Buna MemoryCache diyoruz. Bu sistem, RAM üzerinde çalışır. Çok hızlıdır – çünkü bellekte durur. Ama geçicidir – uygulama kapanırsa silinir. Ve en önemlisi: sınırlı bir kapasitesi vardır.
SizeLimit – Dolabın Kapasitesini Belirlemek
Dolaba “şu kadar şey koyabilirsin” demek bir sınır koymaktır. Aynı şekilde, biz MemoryCache için de bir limit tanımlarız:
bash
var cache = new MemoryCache(new MemoryCacheOptions
{ SizeLimit =100 // Dolabın kapasitesi: 100 birimlik yer
});
Yani bu örnekte: “Benim dolabım (cache), toplamda 100 birimlik veri alabilir” demiş oluyoruz.
Eğer bu limit aşılırsa sistem çökmese bile eski veriler silinir veya hata alınabilir: OutOfMemoryException gibi hatalar oluşabilir.
SetSize – Bu Eşya Kaç Yer Kaplıyor?
Şimdi düşün: Dolaba konulan her eşyanın (mesela kavanoz) kapladığı alanı bilirsen, dolabı çok daha verimli kullanabilirsin değil mi? İşte yazılımda da cache'e veri eklerken, o verinin kaç birim yer kapladığını belirtmemiz gerekir. Buna SetSize denir. Her cache verisi yani her bir “entry” için boyut belirlemek şarttır:
bash
memoryCache.Set("products", productList, new MemoryCacheEntryOptions
{ Size =5 // Bu veri büyük, 5 birim yer kaplasın
});
Kavram
Açıklama
MemoryCache
Dolap (RAM üzerinde çalışan hızlı geçici saklama alanı)
SizeLimit
Dolabın toplam kapasitesi (Kaç birimlik eşya alabileceği)
SetSize
Her bir eşyanın (verinin) kaç birim yer kapladığı
🍱 Örnek Senaryo – Dolabı Dolduralım
Dolap kapasitesi: SizeLimit = 10
Şimdi dolaba veri (eşya) ekliyoruz:
bash
cache.Set("kavanoz1", data1, new MemoryCacheEntryOptions { Size =3});cache.Set("kavanoz2", data2, new MemoryCacheEntryOptions { Size
Her şey güzel gidiyor. Dolap tamamen doldu. Ama sonra bir eşya daha eklemeye çalışıyoruz:
⛔️ Artık sığmaz!
Dolap (cache) dolu olduğu için yeni veriyi ekleyemez.
Bu durumda MemoryCache, en eski veya az kullanılan verileriotomatik olarak çıkarır ve yerine yenisini koyar.
Bu sürece Eviction (taşma ve çıkarma) denir.
Eviction Nedir?
Eviction, dolapta yer kalmadığında eski eşyaların (verilerin) otomatik çıkarılması işlemidir.
Örneğin: “En eski kavanozu at, yenisini koy.” mantığı.
Genel olarak mantığı anladığımızı düşünüyorum . Kavramlara girmeden önce bu örnek üzerinden bu kavramları anlamak için benzetme ile açıklamak bence daha yararlı oluyor.
Umarım anlatabilmişimdir diye düşünüyorum artık kavramlara başlayabiliriz.
🎯 SizeLimit - Cache Boyut Sınırı
Ne İşe Yarar?
Cache'de aynı anda tutabileceğiniz maksimum veri sayısını belirler
Memory kullanımını kontrol altında tutar
İstem dışı bellek tükenmelerini (OutOfMemoryException) engeller
Nasıl Çalışır?
SetSize() belirtilmemişse, her cache girişi 1 birim sayılır
SetSize() ile belirli boyut atarsan, entry (Cache’e eklediğin her bir veri parçasına “entry” denir.) bu değere göre sayılır
Limit aşılırsa, LRU (Least Recently Used) algoritması ile eski veriler silinir
Null atanırsa bu Sınırsız demektir ve tehlikelidir.
Neden Null Tehlikeli?
Memory dolana kadar büyür, uygulama çöker
Sistem kaynaklarını tüketir, diğer uygulamaları etkiler
Production'da asla kullanmayın, sadece test ortamında düşünün
Örnek: 1GB memory'li sistemde cache 2GB'a çıkarsa sistem donar
1GB RAM’li Bir Sistemde Cache 2GB Olabilir mi?
Kısa cevap: Hayır, olamaz (ya da olmamalıdır). Çünkü bu sistemin fiziksel belleğinden (RAM) daha büyük bir alanı cache için ayırmak, OutOfMemoryException (bellek taşması) gibi hatalara ve hatta sistemin tamamen çökmesine yol açabilir.
Örnekler ve Açıklamalar:
bash
// 1. Sınırsız Cache (DİKKATLİ KULLAN!)options.SizeLimit = null;// ❌ NEDEN TEHLİKELİ: Memory dolana kadar büyür, uygulama çöker
// ✅ NE ZAMAN KULLAN: Sadece çok küçük veriler için, test ortamında
// 2. Küçük Uygulamalar (100 entry)options.SizeLimit =100;// ✅ NE İÇİN: Blog siteleri, kişisel projeler
// 📊 MEMORY: ~1-2 MB kullanım
// ⚡ PERFORMANS: Çok hızlı, sürekli temizlik
// 3. Orta Ölçekli Uygulamalar (1024 entry)options.SizeLimit =1024;// ✅ NE İÇİN: E-ticaret siteleri, kurumsal web uygulamaları
// 📊 MEMORY: ~10-20 MB kullanım
// ⚡ PERFORMANS: Dengeli, makul temizlik sıklığı
// 4. Büyük Uygulamalar (10000 entry)options.SizeLimit =10000;// ✅ NE İÇİN: Sosyal medya, büyük e-ticaret platformları
// 📊 MEMORY: ~100-200 MB kullanım
// ⚡ PERFORMANS: Yavaş temizlik, yüksek hit rate
// 5. Enterprise Uygulamalar (50000 entry)options.SizeLimit =50000;// ✅ NE İÇİN: Netflix, Amazon gibi mega platformlar
// 📊 MEMORY: ~500MB-1GB kullanım
// ⚡ PERFORMANS: Çok yavaş temizlik, maksimum cache hit
🎯 SetSize - Entry Boyutu Belirleme
collapsed:: true
Ne İşe Yarar?
Her bir cache entry'nin hafızada kapladığı alanı simgeler
SizeLimit ile birlikte çalışır
Daha gerçekçi ve şeffaf boyut yönetimi sağlar
Nasıl Çalışır?
Entry'e atanırken .SetSize(int) ile kullanılır
SizeLimit'e ulaşıldığında en eski SetSize'li veriler temizlenir
Neden SetSize Kullanırız?
SetSize olmadan: Her entry 1 birim sayılır (büyük/küçük fark etmez)
SetSize ile: Gerçek memory kullanımına göre sayım yapılır
Örnek: 1MB'lık veri ile 1KB'lık veri aynı değer (1 birim)
// 1. Küçük Veri (1KB - 1 birim)cache.Set("user-settings", userSettings, new MemoryCacheEntryOptions
SetSize Olmadan vs SetSize İle:
bash
// ❌ SetSize olmadan (adil değil)cache.Set("tiny-data", tinyData); // 1 birim (gerçekte 1KB)cache.Set(// Gerçek memory kullanımına göre sayım
Memory Yönetimi Senaryoları:
bash
// Senaryo 1: SizeLimit =1000, SetSize kullanılmıyor
// 1000 entry eklenebilir (hepsi 1 birim)// Ama memory kullanımı çok farklı olabilir
🧹 CompactionPercentage - Temizlik Yüzdesi
Ne İşe Yarar?
Cache dolduğunda ne kadar veri silineceğini belirler
Memory temizliğinin ne kadar sert yapılacağını kontrol eder
Cache performansını ve memory kullanımını dengeler
Nasıl Çalışır?
0.0 ile 1.0 arası değer alır (0% ile 100%)
Cache dolduğunda bu yüzde kadar entry silinir
Daha yüksek değer = Daha sert temizlik (daha fazla veri silinir)
Pratik Örnek:
%10 temizlik: 1000 entry varsa sadece 100 tanesi silinir (yumuşak temizlik)
%50 temizlik: 1000 entry varsa 500 tanesi silinir (sert temizlik)
%75 temizlik: 1000 entry varsa 750 tanesi silinir (çok sert temizlik)
Örnekler ve Açıklamalar:
bash
// 1. Conservative Temizlik (%10)options.CompactionPercentage =0.1;// ✅ NE İÇİN: Cache hit rate'i yüksek olması gereken uygulamalar
// 📊 ETKİ: 1000 entry varsa, sadece 100 tanesi silinir
// ⚡ AVANTAJ: Cache'de daha fazla veri kalır
// ❌ DEZAVANTAJ: Sık temizlik yapılır
// 2. Balanced Temizlik (%25) - EN YAYGIN
options.CompactionPercentage =0.25;// ✅ NE İÇİN: Çoğu production uygulaması
// 📊 ETKİ: 1000 entry varsa, 250 tanesi silinir
// ⚡ AVANTAJ: Dengeli performans
// 📈 ÖNERİ: Başlangıç için bu değeri kullanın
// 3. Aggressive Temizlik (%50)options.CompactionPercentage =0.5;// ✅ NE İÇİN: Memory sıkıntısı olan sistemler
// 📊 ETKİ: 1000 entry varsa, 500 tanesi silinir
// ⚡ AVANTAJ: Hızlı memory temizliği
// ❌ DEZAVANTAJ: Cache hit rate düşer
// 4. Very Aggressive Temizlik (%75)options.CompactionPercentage =0.75;// ✅ NE İÇİN: Çok sınırlı memory'li sistemler
// 📊 ETKİ: 1000 entry varsa, 750 tanesi silinir
// ⚡ AVANTAJ: Çok hızlı memory temizliği
// ❌ DEZAVANTAJ: Cache neredeyse boş kalır
⏰ ExpirationScanFrequency - Tarama Sıklığı
Ne İşe Yarar?
Süresi dolmuş cache entry'lerin ne sıklıkla temizleneceğini belirler
CPU kullanımını ve memory temizliğini dengeler
Memory leak'leri önler
Nasıl Çalışır?
Background thread bu sıklıkta çalışır
Expired entry'leri bulup siler
Daha sık tarama = Daha fazla CPU kullanımı ama daha temiz memory
Pratik Örnek:
30 saniye: Her 30 saniyede bir kontrol eder (çok sık, CPU yoğun)
5 dakika: Her 5 dakikada bir kontrol eder (dengeli, önerilen)
30 dakika: Her 30 dakikada bir kontrol eder (yavaş, CPU tasarrufu)
0 (sıfır): Hiç kontrol etmez (tehlikeli, memory leak riski)
Örnekler ve Açıklamalar:
bash
// 1. Çok Hızlı Tarama (30 saniye)options.ExpirationScanFrequency = TimeSpan.FromSeconds(30);// ✅ NE İÇİN: Test ortamları, development
// 📊 CPU KULLANIMI: Yüksek (her 30 saniyede bir tarama)// ⚡ AVANTAJ: Expired entry'ler hemen silinir
// ❌ DEZAVANTAJ: CPU kaynağı tüketir
// 2. Hızlı Tarama (1 dakika)
options.ExpirationScanFrequency = TimeSpan.FromMinutes(1);
// ✅ NE İÇİN: Development, küçük production sistemleri
// 📊 CPU KULLANIMI: Orta-yüksek
// ⚡ AVANTAJ: Hızlı cleanup
// 📈 ÖNERİ: Development için ideal
// 3. Normal Tarama (5 dakika) - EN YAYGIN
options.ExpirationScanFrequency = TimeSpan.FromMinutes(5);
// ✅ NE İÇİN: Çoğu production uygulaması
// 📊 CPU KULLANIMI: Düşük
// ⚡ AVANTAJ: Dengeli CPU kullanımı
// 📈 ÖNERİ: Production için ideal
// 4. Yavaş Tarama (15 dakika)
options.ExpirationScanFrequency = TimeSpan.FromMinutes(15);
// ✅ NE İÇİN: Yüksek performans gerektiren sistemler
// 📊 CPU KULLANIMI: Çok düşük
// ⚡ AVANTAJ: Minimum CPU kullanımı
// ❌ DEZAVANTAJ: Expired entry'ler uzun süre memory'de kalır
// 5. Çok Yavaş Tarama (30 dakika)
options.ExpirationScanFrequency = TimeSpan.FromMinutes(30);
// ✅ NE İÇİN: CPU kritik sistemler
// 📊 CPU KULLANIMI: Minimal
// ⚡ AVANTAJ: Maksimum CPU tasarrufu
// ❌ DEZAVANTAJ: Memory leak riski
// 6. Taramayı Devre Dışı Bırak
options.ExpirationScanFrequency = TimeSpan.Zero;
// ❌ NEDEN TEHLİKELİ: Expired entry'ler hiç silinmez
// ✅ NE ZAMAN: Sadece özel durumlar için
📈 İstatistik ve İzleme Options (.NET 8.0+)
📊 TrackStatistics - İstatistik Toplama
Ne İşe Yarar?
Cache hit/miss oranlarını takip eder
Performance monitoring için kritik veriler sağlar
Cache stratejinizi optimize etmenize yardım eder
Nasıl Çalışır?
Her cache operasyonunu sayar
Hit: Cache'den veri bulundu (başarılı)
Miss: Cache'de veri bulunamadı (başarısız)
Ratio = Hit / (Hit + Miss) (başarı oranı)
Pratik Örnek:
Hit Ratio %90: Cache çok iyi çalışıyor, veriler çoğunlukla cache'den geliyor
Hit Ratio %50: Cache orta düzeyde çalışıyor, yarı yarıya
Hit Ratio %10: Cache kötü çalışıyor, çoğunlukla database'den geliyor
Hit Ratio %0: Cache hiç çalışmıyor, tüm veriler database'den geliyor
Örnekler ve Açıklamalar:
bash
// 1. İstatistikleri Aktif Et
options.TrackStatistics =true;// ✅ NE İÇİN: Production sistemleri, monitoring gerektiren uygulamalar
// 📊 VERİLER: Hit count, miss count, hit ratio
// ⚡ AVANTAJ: Performance analizi yapabilirsiniz
// 📈 ÖNERİ: Production'da mutlaka aktif edin
// 2. İstatistikleri Devre Dışı Bırak
options.TrackStatistics = false;
// ✅ NE İÇİN: Çok küçük uygulamalar, test ortamları
// 📊 VERİLER: Hiçbir istatistik toplanmaz
// ⚡ AVANTAJ: Hafif memory footprint
// ❌ DEZAVANTAJ: Performance analizi yapamazsınız
// KULLANIM ÖRNEĞİ:
var stats = memoryCache.GetCurrentStatistics();
if (stats != null)
{
Console.WriteLine($"Cache Hit Count: {stats.TotalHits}");
Console.WriteLine($"Cache Miss Count: {stats.TotalMisses}");
double hitRatio = (double)stats.TotalHits / (stats.TotalHits + stats.TotalMisses);
Console.WriteLine($"Cache Hit Ratio: {hitRatio:P2}");
// Hit ratio %80'in altındaysa cache stratejinizi gözden geçirin
if(hitRatio <0.8){ Console.WriteLine("⚠️ Cache hit ratio düşük! Stratejinizi optimize edin.");}}
🔗 TrackLinkedCacheEntries - Linked Entry Takibi
Ne İşe Yarar?
Cache entry'leri arasındaki bağlantıları takip eder
Bir entry silindiğinde bağlı entry'leri de silmenizi sağlar
Cache invalidation stratejileri için kullanışlı
Nasıl Çalışır?
Entry'ler arasında parent-child ilişkisi kurar
Parent silindiğinde child'lar da silinir
Chain invalidation için idealdir
Pratik Örnek:
User silindiğinde → User'ın tüm cache'leri de silinir
Product silindiğinde → Product'ın tüm cache'leri de silinir
Category silindiğinde → Category'nin tüm cache'leri de silinir
Bu sayede tutarsız veri kalmaz, cache her zaman güncel kalır
Örnekler ve Açıklamalar:
bash
// 1. Linked Entry Tracking Aktif
options.TrackLinkedCacheEntries =true;// ✅ NE İÇİN: Karmaşık cache stratejileri, dependency management
// 📊 ÖRNEK: User silindiğinde user'ın tüm cache'leri de silinir
// ⚡ AVANTAJ: Otomatik cleanup, tutarlılık
// ❌ DEZAVANTAJ: Hafif performance overhead
// 2. Linked Entry Tracking Devre Dışı
options.TrackLinkedCacheEntries =false;// ✅ NE İÇİN: Basit cache stratejileri, performance kritik sistemler
// 📊 ÖRNEK: Her entry bağımsız olarak yönetilir
// ⚡ AVANTAJ: Maksimum performance
// ❌ DEZAVANTAJ: Manuel cleanup gerekir
// KULLANIM ÖRNEĞİ:
// User ve user'ın order'ları arasında link kurma
var userKey ="user:123";var userOrdersKey ="user:123:orders";// User'ı cache'le
memoryCache.Set(userKey, user, new MemoryCacheEntryOptions
{ Size =1,
Priority = CacheItemPriority.Normal
});// User'ın order'larını cache'le ve user'a link'le
memoryCache.Set(userOrdersKey, orders, new MemoryCacheEntryOptions
{ Size =1,
Priority = CacheItemPriority.Normal,
// User silindiğinde orders da silinir
PostEvictionCallbacks ={ new PostEvictionCallbackRegistration
{ EvictionCallback =(key, value, reason, state)=>{if(key.ToString()== userKey){ memoryCache.Remove(userOrdersKey);}}}}});
⚡ Tüm Redis Cache Options Detayları
🔗 Temel Bağlantı Options
🌐 Configuration - Bağlantı String'i
Ne İşe Yarar?
Redis sunucusuna nasıl bağlanacağınızı belirler
Tek satırda tüm bağlantı ayarlarını yapmanızı sağlar
En basit ve hızlı Redis konfigürasyon yöntemidir
Nasıl Çalışır?
Connection string formatı: host:port[,options]
Virgülle ayrılmış key-value çiftleri
Redis client bu string'i parse eder ve bağlantı kurar
Pratik Örnek:
localhost:6379 → Local Redis'e bağlanır
redis.company.com:6380,ssl=true,password=mykey → Güvenli remote bağlantı
Environment variable kullanarak güvenli hale getirin
Örnekler ve Açıklamalar:
bash
// 1. Basit Local Bağlantı
options.Configuration ="localhost:6379";// ✅ NE İÇİN: Development, test ortamları
// 📊 ÖZELLİKLER: Şifresiz, SSL yok, default port
// ⚡ AVANTAJ: En basit kurulum
// ❌ DEZAVANTAJ: Güvenlik yok, sadece local// 2. Şifreli Local Bağlantı
options.Configuration ="localhost:6379,password=mypassword";// ✅ NE İÇİN: Development, küçük production sistemleri
// 📊 ÖZELLİKLER: Authentication var, SSL yok
// ⚡ AVANTAJ: Basit güvenlik
// 🔐 GÜVENLİK: Şifre connection string'de (environment variable kullanın!)// 3. SSL Remote Bağlantı
options.Configuration ="redis.company.com:6380,ssl=true,password=mykey";// ✅ NE İÇİN: Production sistemleri, cloud Redis
// 📊 ÖZELLİKLER: SSL/TLS encryption, authentication
// ⚡ AVANTAJ: Güvenli bağlantı
// 📈 ÖNERİ: Production için ideal
// ✅ NE İÇİN: Yüksek availability gerektiren sistemler
// 📊 ÖZELLİKLER: Multiple endpoints, failover support
// ⚡ AVANTAJ: High availability
// ❌ DEZAVANTAJ: Karmaşık setup
// ✅ NE İÇİN: Azure cloud uygulamaları
// 📊 ÖZELLİKLER: Managed Redis, SSL, authentication
// ⚡ AVANTAJ: Managed service, otomatik backup
// 💰 MALİYET: Azure pricing
// ✅ NE İÇİN: AWS cloud uygulamaları
// 📊 ÖZELLİKLER: AWS managed Redis, SSL
// ⚡ AVANTAJ: AWS integration, auto-scaling
// 💰 MALİYET: AWS pricing
// ✅ NE İÇİN: Production sistemleri
// 📊 ÖZELLİKLER: Güvenli credential management
// ⚡ AVANTAJ: Şifreler kodda yok
// 📈 ÖNERİ: En güvenli yöntem
🏷️ InstanceName - Key Prefix Sistemi
Ne İşe Yarar?
Redis'teki tüm key'lerin başına prefix ekler
Farklı uygulamaların aynı Redis'i kullanmasını sağlar
Key collision'larını önler
Nasıl Çalışır?
Uygulama: "user:123" key'i kullanır
Redis'te gerçek key: "MyApp:user:123" olur
InstanceName + ":" + OriginalKey formatı
Pratik Örnek:
MyApp:user:123 → Web uygulamasının user cache'i
MyApp-API:user:123 → API uygulamasının user cache'i
MyApp-BG:user:123 → Background service'in user cache'i
Hepsi aynı Redis'te ama birbirini etkilemez
Örnekler ve Açıklamalar:
bash
// 1. Production Prefix
options.InstanceName ="MyApp";// ✅ NE İÇİN: Production ortamları
// 📊 ÖRNEK: user:123 → MyApp:user:123
// ⚡ AVANTAJ: Temiz ve anlaşılır
// 📈 ÖNERİ: Production için ideal
// 2. Development Prefix
options.InstanceName ="MyApp-Dev";// ✅ NE İÇİN: Development ortamları
// 📊 ÖRNEK: user:123 → MyApp-Dev:user:123
// ⚡ AVANTAJ: Dev/Prod ayrımı
// 🔍 DEBUGGING: Redis'te hangi key'in dev'e ait olduğu belli
// 3. Test Prefix
options.InstanceName = "MyApp-Test";
// ✅ NE İÇİN: Test ortamları
// 📊 ÖRNEK: user:123 → MyApp-Test:user:123
// ⚡ AVANTAJ: Test verileri ayrı
// 🧪 TESTING: Test verileri production'ı etkilemez
// 4. Staging Prefix
options.InstanceName ="MyApp-Staging";// ✅ NE İÇİN: Staging ortamları
// 📊 ÖRNEK: user:123 → MyApp-Staging:user:123
// ⚡ AVANTAJ: Pre-production testing
// ✅ NE İÇİN: Multi-tenant uygulamalar
// 📊 ÖRNEK: user:123 → MyApp-TenantA:user:123
// ⚡ AVANTAJ: Tenant isolation
// Web API: MyApp-API:user:123
// Background Service: MyApp-BG:user:123
// Admin Panel: MyApp-Admin:user:123
⚙️ Gelişmiş Konfigürasyon Options
🔧 ConfigurationOptions - Detaylı Redis Ayarları
Ne İşe Yarar?
Connection string'den daha detaylı Redis konfigürasyonu sağlar
Programmatic olarak tüm Redis ayarlarını kontrol etmenizi sağlar
Karmaşık Redis setup'ları için gereklidir
Nasıl Çalışır?
Configuration property'si yerine kullanılır
Daha fazla kontrol ve esneklik sağlar
Type-safe configuration yöntemidir
Örnekler ve Açıklamalar:
bash
options.ConfigurationOptions = new ConfigurationOptions
{ // 🌐 BAĞLANTI YÖNETİMİ
EndPoints ={"redis1.company.com:6379",
"redis2.company.com:6379",
"redis3.company.com:6380"},
// ✅ NE İÇİN: Redis cluster, high availability
// 📊 ÖZELLİKLER: Multiple server support, failover
// ⚡ AVANTAJ: Bir server down olsa diğerleri çalışır
// ❌ DEZAVANJ: Karmaşık setup gerektirir
Password = Environment.GetEnvironmentVariable("REDIS_PASSWORD"),
// ✅ NE İÇİN: Production sistemleri
// 📊 ÖZELLİKLER: Güvenli credential management
// ⚡ AVANTAJ: Şifre kodda yok
// 📈 ÖNERİ: Environment variable kullanın
Database = // ✅ NE İÇİN: Veri organizasyonu
// ⚡ AVANTAJ: Sync operasyonlar için optimize
// 📈 ÖNERİ: Async operasyonlar tercih edin
// ✅ NE İÇİN: Asynchronous Redis operasyonları
// ✅ NE İÇİN: Custom SSL sertifikaları
// 📊 ÖZELLİKLER: Certificate validation
// ⚡ AVANTAJ: Man-in-the-middle saldırılarına karşı koruma
// 📈 ÖNERİ: Wildcard sertifikalar için kullanın
// ✅ NE İÇİN: Production güvenliği
// 📊 ETKİ: FLUSHDB, CONFIG gibi admin komutları engellenir
// ⚡ AVANTAJ: Güvenlik riski azalır
// ⚡ PERFORMANS
// ✅ NE İÇİN: Uzun süreli bağlantılar
// ✅ NE İÇİN: Database seçimi
// 📊 ETKİ: Bağlantı kurulduğunda otomatik bu database seçilir
// ⚡ AVANTAJ: Her seferinde SELECT komutu çalıştırmaya gerek yok
// ✅ NE İÇİN: Monitoring ve debugging
// ✅ NE İÇİN: Network partition durumları
// 📊 ETKİ: Hangi master'ın aktif olacağını belirler
// ⚡ AVANTAJ: Data consistency
// 📈 ÖNERİ: Sentinel ile birlikte kullanın
// 🌐 NETWORK
// ✅ NE İÇİN: Dynamic DNS environments
// 📊 ETKİ: DNS değişikliklerini otomatik takip eder
// ⚡ AVANTAJ: IP değişikliklerinde otomatik adaptasyon
// 📈 ÖNERİ: Cloud environments için ideal
// ✅ NE İÇİN: Performance optimization
// 📊 ETKİ: SSL sertifika iptal listesi kontrol edilmez
// ⚡ AVANTAJ: Daha hızlı SSL handshake
// ❌ DEZAVANJ: İptal edilmiş sertifikalar tespit edilmez
🎯 Farklı Senaryolar İçin ConfigurationOptions Örnekleri
1. Development Environment:
bash
options.ConfigurationOptions = new ConfigurationOptions
{ EndPoints ={"localhost:6379"},
ConnectTimeout =2000, // Hızlı bağlantı
SyncTimeout =2000, // Hızlı sync AsyncTimeout =3000, // Hızlı async
ConnectRetry =2, // Az retry
AbortOnConnectFail = true, // Hızlı hata tespiti
Ssl = false, // SSL yok (local) KeepAlive =60, // Kısa keep-alive
ClientName ="MyApp-Dev"};
2. Production Environment:
bash
options.ConfigurationOptions = new ConfigurationOptions
{ EndPoints ={"redis1.prod.company.com:6379",
3. High Performance Environment:
bash
options.ConfigurationOptions = new ConfigurationOptions
{ EndPoints ={"redis-cluster.company.com:6379"},
Password =
// Özel bağlantı mantığı
options.ConnectionMultiplexerFactory = async ()=>{ var config = new ConfigurationOptions
{ EndPoints ={ GetRedisEndpoint()}, // Dynamic endpoint
Password = await GetRedisPasswordFromVault(), // Güvenli şifre alma
ConnectTimeout =5000,
ConnectRetry =3}; // Custom retry logic
var multiplexer = await ConnectionMultiplexer.ConnectAsync(config); // Connection event handlers
multiplexer.ConnectionFailed +=(sender, args)=>{ Console.WriteLine($"Redis connection failed: {args.Exception}");}; multiplexer.ConnectionRestored +=(sender, args)=>{ Console.WriteLine("Redis connection restored");};return multiplexer;};
ProfilingSession: Performance profiling
bash
// Redis komut profiling'i
options.ProfilingSession =()=>{return new ProfilingSession(); // Her request için yeni session
// Kullanım:
🎯 Environment-Specific Optimal Konfigürasyonlar
🔧 Development Environment
bash
// Memory Cache - Development
options.SizeLimit =100;options.CompactionPercentage =0.5;options.ExpirationScanFrequency = TimeSpan.FromMinutes(1);options.TrackStatistics =true;// Redis Cache - Development
options.Configuration ="localhost:6379";options.InstanceName ="MyApp-Dev";options.ConfigurationOptions.ConnectTimeout =2000;options.ConfigurationOptions.SyncTimeout =2000;
🚀 Production Environment
bash
// Memory Cache - Production
options.SizeLimit =5000;options.CompactionPercentage =0.25;options.ExpirationScanFrequency = TimeSpan.FromMinutes(10);options.TrackStatistics =true;// Redis Cache - Production
options.Configuration ="redis-cluster.company.com:6379,ssl=true";options.InstanceName ="MyApp-Prod";options.ConfigurationOptions.ConnectRetry =5;options.ConfigurationOptions.ReconnectRetryPolicy = new ExponentialRetry(1000);options.ConfigurationOptions.KeepAlive =300;
⚡ High Performance Environment
bash
// Memory Cache - High Performance
options.SizeLimit =20000;options.CompactionPercentage =0.1;options.ExpirationScanFrequency = TimeSpan.FromMinutes(30);options.TrackLinkedCacheEntries =false; // Performance için devre dışı
// Redis Cache - High Performance
options.ConfigurationOptions.AsyncTimeout =1000;options.ConfigurationOptions.SyncTimeout =1000;options.ConfigurationOptions.ConnectTimeout =1000;options.ConfigurationOptions.KeepAlive =60;
📋 Cache Konfigürasyon Parametreleri Detayları
Cache konfigürasyonundaki her parametre, sistemin performansını ve güvenilirliğini doğrudan etkiler. Bu parametrelerin doğru anlaşılması ve optimize edilmesi, production ortamlarında karşılaşılabilecek sorunları önlemek için kritik önem taşır.
🧠 In-Memory Cache Parametreleri
SizeLimit (1024) - Cache'de tutulabilecek maksimum entry sayısını belirler. Bu sınır aşıldığında cache compaction (temizlik) işlemi başlar. Örnekler:
SizeLimit = 1024: Cache'de en fazla 1024 adet cache entry tutulabilir
SizeLimit = 500: Küçük uygulamalar için uygun, daha sık compaction yapılır
CompactionPercentage (0.25) - Cache boyutu sınıra ulaştığında ne kadar temizlik yapılacağını belirler. %25 değeri, cache'in %25'inin silineceği anlamına gelir:
CompactionPercentage = 0.10: Cache dolduğunda %10'u temizlenir (daha az temizlik)
ExpirationScanFrequency (5 dakika) - Süresi dolmuş cache entry'lerin ne sıklıkla taranıp silineceğini belirler:
TimeSpan.FromMinutes(5): Her 5 dakikada bir expired entry'ler taranır
TimeSpan.FromMinutes(1): Daha sık tarama (daha fazla CPU kullanımı)
TimeSpan.FromMinutes(15): Daha az tarama (memory'de expired entry'ler daha uzun kalır)
🔄 Redis Distributed Cache Parametreleri
InstanceName ("MyApp") - Bu parametre çok kritiktir çünkü Redis'teki tüm key'lerin başına prefix olarak eklenir. Bu sayede farklı uygulamalar aynı Redis instance'ını paylaşabilir ve key collision'ları önlenir:
text
// InstanceName = "MyApp" olduğunda:
Uygulama cache key'i: "user:123"
Redis'teki gerçek key: "MyApp:user:123"
// Başka bir uygulama InstanceName = "BlogApp" kullanırsa:
Uygulama cache key'i: "user:123"
Redis'teki gerçek key: "BlogApp:user:123"
// Bu sayede iki uygulama aynı Redis'i kullanabilir
Key Prefix Neden Önemli:
Namespace Separation: Farklı uygulamalar arasında key collision'ını önler
Multi-tenant Support: Aynı Redis cluster'da birden fazla uygulama çalışabilir
Environment Isolation: Dev, Test, Prod ortamları ayrı prefix'ler kullanabilir
Debugging Kolaylığı: Redis'te hangi key'in hangi uygulamaya ait olduğu kolayca anlaşılır
Configuration: Redis bağlantı string'i. Karmaşık Redis kurulumları için detaylı configuration gerekebilir:
csharp
// Basit bağlantı"Redis":"localhost:6379"// Authentication ile"Redis":"localhost:6379,password=mypassword"// Cluster setup"Redis":"redis1:6379,redis2:6379,redis3:6379"// SSL ile"Redis":"myredis.cache.windows.net:6380,ssl=true,password=mykey"
Konfigürasyon dosyasında cache ayarlarının merkezi olarak yönetilmesi, farklı environment'larda farklı cache stratejilerinin kolayca uygulanmasını sağlar. Özellikle development'ta in-memory, production'da Redis kullanımı yaygın bir yaklaşımdır.
Monolit mimariler, özellikle orta ölçekli uygulamalarda yaygın olarak tercih edilir. Bu yaklaşımda cache implementasyonu nispeten basittir ancak doğru organize edilmediğinde kod karmaşıklığına yol açabilir. Bu bölümde temiz ve sürdürülebilir bir cache implementasyonu nasıl yapılacağını detaylı olarak inceleyeceğiz.
Cache servisleri organize edilirken, separation of concerns prensibi göz önünde bulundurulmalıdır. Interface'ler, implementasyonlar ve configuration'lar ayrı ayrı dosyalarda tutularak kod okunabilirliği ve test edilebilirliği artırılır.
Interface tasarımında async/await pattern'inin kullanılması, özellikle distributed cache operasyonlarında performans açısından kritik önem taşır. Generic metodlar sayesinde tip güvenliği sağlanırken, opsiyonel parametreler ile esneklik kazanılır.
bash
// Services/Interfaces/ICacheService.cs
public interface ICacheService
{ Task<T?> GetAsync<T>(string key) where T : class; Task SetAsync<T>(string key, T value, TimeSpan? expiration = null) where T : class; Task RemoveAsync(string key); Task RemoveByPatternAsync(string pattern); Task<bool> ExistsAsync(string key); Task<IEnumerable<string>> GetKeysAsync(string pattern); Task ClearAllAsync();}
🔨 Hybrid Cache Service Implementation
Hybrid cache yaklaşımı, hem in-memory hem de distributed cache'in avantajlarını birleştiren güçlü bir stratejidir. L1 (Memory) cache hızlı erişim sağlarken, L2 (Redis) cache distributed ortamlarda tutarlılık sağlar. Bu implementasyonda cache miss durumlarında akıllı fallback mekanizmaları kullanılır.
Extension metodları sayesinde cache servislerinin registration işlemi tek bir metodla yapılabilir. Bu yaklaşım hem kod tekrarını önler hem de yapılandırma merkezi olarak yönetilir. Conditional registration sayesinde farklı environment'larda farklı cache türleri aktif edilebilir.
bash
// Extensions/ServiceCollectionExtensions.cs
public static class ServiceCollectionExtensions
{ public static IServiceCollection AddCachingServices( this IServiceCollection services,
IConfiguration configuration){ // Cache ayarlarını configuration'dan al
var cacheSettings = configuration.GetSection("CacheSettings");
var useDistributedCache = cacheSettings.GetValue<bool>("UseDistributedCache");
// Memory Cache her zaman eklenir (L1 Cache olarak)
services.AddMemoryCache(options =>
{
var memoryCacheSettings = cacheSettings.GetSection("MemoryCacheSettings");
options.SizeLimit = memoryCacheSettings.GetValue<int>("SizeLimit", 1024);
options.CompactionPercentage = memoryCacheSettings.GetValue<double>("CompactionPercentage", 0.25);
options.ExpirationScanFrequency = TimeSpan.FromMinutes(
memoryCacheSettings.GetValue<int>("ExpirationScanFrequencyMinutes", 5));
📚 Shared Klasöründe Organize Yapı
Büyük projelerde ve team çalışmalarında cache implementasyonunun organize edilmesi, kod kalitesi ve sürdürülebilirlik açısından kritik önem taşır. Shared klasör yapısı sayesinde cache servisleri merkezi olarak yönetilir ve farklı projeler arasında paylaşılabilir hale gelir.
Bu yaklaşımda cache ile ilgili tüm bileşenler (interfaces, services, configuration, extensions) ayrı klasörlerde organize edilir. Böylece geliştiriciler cache-related kod parçalarını kolayca bulabilir ve modify edebilir. Ayrıca bu yapı, automated testing ve documentation açısından da avantajlar sağlar.
Cache key'lerinin tutarlı ve collision-free olarak üretilmesi, cache sisteminin güvenilirliği için hayati önem taşır. Key generator servisi, naming convention'ları uygular ve key collision'larını önler. Hierarchical key yapısı sayesinde cache invalidation işlemleri daha kolay yönetilebilir.
Attribute-based caching, declarative programming paradigmasını cache implementasyonuna taşır. Method seviyesinde cache tanımları yapılarak, cross-cutting concern'ler business logic'ten ayrılır. AOP (Aspect-Oriented Programming) ile birlikte kullanıldığında güçlü cache stratejileri uygulanabilir.
bash
// Shared/Caching/Attributes/CacheableAttribute.cs
[AttributeUsage(AttributeTargets.Method)]public class CacheableAttribute : Attribute
{ public string? KeyPrefix { get;set;} public int ExpirationMinutes { get;set;}=30; public bool UseDistributedCache { get;set;}=true; public string[]? Tags { get;set;} public CacheItemPriority Priority { get;set;}= CacheItemPriority.Normal; public bool IncludeParameters { get;set;}=true; /// <summary> /// Cache condition expression (SpEL benzeri) /// Örnek: "args[0] != null && args[0].IsActive" /// </summary> public string? Condition { get;set;}}// Shared/Caching/Attributes/CacheEvictAttribute.cs
[AttributeUsage(AttributeTargets.Method)]public class CacheEvictAttribute : Attribute
{ public string? KeyPrefix { get;set;} public string[]? Keys { get;set;} public string[]? Tags { get;set;} public bool AllEntries { get;set;}=false; public bool BeforeInvocation { get;set;}=false;}
🧅 Onion Architecture'de Cache Implementasyonu
Onion Architecture, bağımlılıkların dışarıdan içeriye doğru yönlendirildiği temiz bir mimari yaklaşımdır. Bu mimaride cache implementasyonu, Infrastructure katmanında yer alırken, Application katmanı cache interface'lerini tanımlar. Bu sayede cache technology'si değiştirildiğinde sadece Infrastructure katmanı etkilenir.
Domain-driven design prensipleri ile cache implementasyonu birleştirildiğinde, hem temiz kod hem de yüksek performans elde edilir. Cache servisleri, domain entity'leri üzerinde çalışırken, persistence ignorance prensibi korunur.
Application layer'da tanımlanan interface'ler, cache implementasyonunun contract'ını belirler. Bu interface'ler domain-agnostic olarak tasarlanır ve infrastructure bağımlılığı taşımaz. Generic constraintler sayesinde tip güvenliği sağlanırken, async metodlar performance optimizasyonu sağlar.
Infrastructure layer'da cache implementation'ı, Application layer'da tanımlanan contract'ları implement eder. Bu katmanda Redis, MongoDB, SQL Server gibi concrete technology'ler kullanılır. Dependency Inversion principle sayesinde Application layer bu implementasyon detaylarından bağımsızdır.
bash
// Infrastructure/Caching/Services/RedisCacheService.cs
using MyApp.Core.Application.Interfaces.Services;using Microsoft.Extensions.Caching.Distributed;using System.Text.Json;namespace MyApp.Infrastructure.Caching.Services;public class RedisCacheService : ICacheService
{ private readonly IDistributedCache _distributedCache; private readonly ILogger<RedisCacheService> _logger; private readonly JsonSerializerOptions _jsonOptions IDistributedCache distributedCache,
try
try
try
try
try
// Redis tag-based invalidation için Redis Set kullanılabilir
// Bu implementasyon StackExchange.Redis ile özel tag management gerektirir
try
try
🔑 Cache Key Service Implementation
Cache key servisi, Onion Architecture'de domain entity'leri ve value object'leri ile çalışırken, clean naming convention'ları uygular. Entity ID'leri, aggregate root'ları ve domain event'leri için standardize edilmiş key generation stratejileri sunar.
bash
// Infrastructure/Caching/Services/CacheKeyService.cs
using MyApp.Core.Application.Interfaces.Services;using MyApp.Core.Domain.Common;namespace MyApp.Infrastructure.Caching.Services;public class CacheKeyService : ICacheKeyService
{ private const string SEPARATOR =":"; private readonly string _applicationPrefix; private readonly string _environmentPrefix; public CacheKeyService(IConfiguration configuration)
🗄️ Repository Design Pattern'da Cache Kullanımı
Repository pattern ile cache entegrasyonu, data access layer'da transparent caching sağlar. Repository'ler cache-aware hale getirilirken, business logic cache implementasyon detaylarından bağımsız kalır. Bu yaklaşım read-heavy uygulamalarda önemli performans kazançları sağlar.
Cache-aside pattern implementasyonu ile repository'ler önce cache'i kontrol eder, miss durumunda database'e gider ve sonucu cache'ler. Write operasyonlarında ise cache invalidation stratejileri devreye girer. Bu sayede data consistency korunurken performance maximize edilir.
🎯 Generic Repository Interface
Generic repository interface'i, CRUD operasyonlarını cache desteği ile genişletir. UseCache parametreleri sayesinde selective caching yapılabilir. Bu esneklik, farklı entity'ler için farklı cache stratejilerinin uygulanmasını sağlar.
Repository implementasyonunda cache-aside pattern uygulanır. Her read operasyonunda önce cache kontrol edilir, miss durumunda database'den veri alınıp cache'lenir. Write operasyonlarında related cache entries invalidate edilir. Bu implementasyon hem performance hem de consistency sağlar.
Cache implementasyonunda doğru stratejiler uygulamak, hem performans hem de sistem güvenilirliği açısından kritik önem taşır. Bu bölümde production ortamlarında test edilmiş best practice'leri ve kaçınılması gereken anti-pattern'leri detaylı olarak inceleyeceğiz.
Effective caching, sadece doğru kod yazmaktan ibaret değildir. Cache key naming, expiration strategies, invalidation patterns, error handling ve monitoring gibi konularda systematic approach uygulamak gerekir. Ayrıca security considerations ve performance monitoring da cache implementasyonunun ayrılmaz parçalarıdır.
✅ Cache Best Practices
Kategori
Best Practice
Açıklama
Key Naming
Consistent naming convention kullanın
app:env:entity:id formatı tercih edin
Expiration Strategy
Uygun TTL değerleri belirleyin
Frequently changing data için kısa, static data için uzun
Cache Invalidation
Write operasyonlarında cache'i temizleyin
Add/Update/Delete sonrası ilgili cache'leri invalidate edin
Error Handling
Cache hatalarında application'ı durdurmayın
Cache miss durumunda data source'a fallback yapın
Memory Management
Cache boyutunu monitör edin
Memory leak'leri önlemek için size limit belirleyin
Serialization
Lightweight serialization kullanın
JSON.NET yerine System.Text.Json tercih edin
Performance Monitoring
Cache hit/miss oranlarını takip edin
Application Insights veya custom metrics kullanın
Security
Sensitive data'yı cache'lemeyin
PII ve credentials cache'den uzak tutun
Concurrency
Race condition'lara dikkat edin
Lock-free pattern'ler tercih edin
Testing
Cache behavior'ını test edin
Unit test'lerde cache mock'ları kullanın
🚫 Cache Anti-Patterns
Cache implementasyonunda sık yapılan hatalar system performance'ını olumsuz etkiler ve maintenance maliyetlerini artırır. Bu anti-pattern'leri bilmek ve kaçınmak, robust cache sistemleri geliştirmek için kritik önem taşır.
Anti-Pattern
Neden Kötü
Doğru Yaklaşım
Cache-aside olmadan kullanım
Veri tutarsızlığı yaratır
Cache-aside pattern uygulayın
Çok uzun TTL değerleri
Stale data sorununa yol açar
İş ihtiyacına uygun TTL belirleyin
Cache'de sensitive data
Güvenlik riski oluşturur
Hassas verileri cache'lemeyin
Büyük objeleri cache'leme
Memory tüketimi artar
Gerçekten gerekli olan alanları tutun
Exception'ları swallow etmek
Debug zorlaşır
Logla ama application'ı durdurmayın
Cache stampede
Aynı veri için concurrent request'ler
Lock mechanism veya queue kullanın
Hot key problem
Tek key'e çok fazla erişim
Sharding veya local cache kullanın
Cache dependency hell
Karmaşık invalidation dependencies
Simple invalidation strategy uygulayın
🔧 Configuration Best Practices
Production-ready cache configuration, systematic approach gerektirir. Environment-specific settings, connection pooling, timeout configurations ve fallback mechanisms doğru planlanmalıdır.
bash
// Optimal Configuration Örneği
public class CacheConfiguration
{ public class MemoryCacheConfig
{ public int SizeLimit { get;set;}=1024; public double CompactionPercentage { get;set;}=0.25; public TimeSpan ExpirationScanFrequency public class RedisCacheConfig
public class CacheSettings
public class CacheStrategies
📊 Performance Monitoring
Cache performansının sürekli monitör edilmesi, sistem optimizasyonu için kritik veriler sağlar. Hit/miss ratios, response times, memory usage ve error rates gibi metrics systematic olarak takip edilmelidir.
bash
// Cache Metrics Service
public interface ICacheMetricsService
{ void RecordCacheHit(string cacheType, string operation, TimeSpan duration); void RecordCacheMiss(string cacheType, string operation); void RecordCacheSetTime(string cacheType, string operation, TimeSpan duration); void RecordCacheError(string cacheType, string operation, Exception exception); Task<CacheStatistics> GetStatisticsAsync(TimeSpan timeWindow private class CacheOperationStats
public class CacheStatistics
🎉 Sonuç
Bu kapsamlı rehberde C# ile cache implementasyonunu farklı mimari yaklaşımlarında nasıl uygulayacağınızı detaylı örneklerle inceledik. In-Memory caching'den Redis distributed caching'e, monolit mimaridenOnion Architecture'a, temel implementasyonlardanRepository pattern entegrasyonuna kadar geniş bir yelpazede pratik çözümler sunduk.
Modern .NET uygulamalarında caching, artık luxury değil necessity haline gelmiştir. Yüksek trafikli sistemlerde cache stratejilerinin doğru planlanması, hem user experience hem de infrastructure cost açısından kritik farklar yaratır. Bu rehberde sunduğumuz pattern'ler ve best practice'ler, production ortamlarında test edilmiş, scalable çözümlerdir.
🔑 Önemli Takeaway'ler:
Hybrid approach ile hem memory hem de distributed cache'in avantajlarını birleştirerek, optimal performance elde edebilirsiniz. L1 (Memory) cache fast access sağlarken, L2 (Redis) cache distributed consistency garantiler. Bu kombinasyon especially microservice architectures'da güçlü sonuçlar verir.
Cache invalidation stratejinizi design phase'den itibaren planlayın. Write operasyonlarından sonra hangi cache entry'lerinin invalidate edileceği, cache key hierarchy'si ve dependencies açık şekilde tanımlanmalıdır. Aksi takdirde stale data problemleri user experience'ı olumsuz etkiler.
Error handling ile cache hatalarının uygulamanızı etkilemesini engelleyin. Cache her zaman available olmayabilir, network issues yaşanabilir. Bu durumlar için robust fallback mechanisms implement etmek, system reliability açısından kritiktir.
Monitoring ile cache performansını sürekli takip edin. Hit/miss ratios, response times, memory usage gibi metrics'leri track ederek, cache strategy'nizi optimize edin. Production'da cache behavior'ının analysis'i için comprehensive logging ve metrics collection essential'dır.
Key naming convention ile organizasyonu sağlayın. Hierarchical key structures, bulk invalidation operations'ı kolaylaştırır ve cache management'ı simplify eder. Consistent naming patterns team collaboration'ını da improve eder.
Bu teknikleri projelerinizde uygulayarak hem performansı artırabilir hem de ölçeklenebilirlik kazanabilirsiniz. Cache'leme stratejinizi uygulamanızın ihtiyaçlarına göre adapte etmeyi unutmayın! Remember, premature optimization is the root of all evil, ama well-planned caching is the foundation of all scalability.
Her cache implementation'ı unique requirements'lara sahiptir. Bu rehberde sunduğumuz foundation'ı base alarak, specific use case'lerinize uygun customization'lar yapabilirsiniz. Happy caching!
Version
=
"2.7.20"
/
>
=
0.25
;
// Temizlik oranı
(
%25
)
options.ExpirationScanFrequency
=
TimeSpan.FromMinutes
(
5
)
;
// Temizlik sıklığı
options.TrackStatistics
=
true
;
// Cache istatistiklerini topla
(
.NET
8.0
+
)
options.TrackLinkedCacheEntries
=
true
;
// Linked entry'leri takip et
(
.NET
8.0
+
)
}
)
;
builder.Services.AddStackExchangeRedisCache
(
options
=
>
{
options.Configuration
=
builder.Configuration.GetConnectionString
(
"Redis"
)
;
options.InstanceName
=
"MyApp"
;
// Redis instance adı
(
key prefix
)
options.ConfigurationOptions
=
new ConfigurationOptions
{
EndPoints
=
{
"localhost:6379"
,
"localhost:6380"
}
, // Cluster endpoints
Password
=
"mypassword"
, // Authentication
Database
=
0
, // Redis database numarası
ConnectTimeout
=
5000
, // Bağlantı
timeout
(
ms
)
SyncTimeout
=
5000
, // Sync operation
timeout
(
ms
)
AsyncTimeout
=
5000
, // Async operation
timeout
(
ms
)
ConnectRetry
=
3
, // Bağlantı retry sayısı
AbortOnConnectFail
=
false, // Bağlantı hatada abort etme
AllowAdmin
=
false, // Admin komutlarına izin
Ssl
=
false, // SSL kullanımı
SslHost
=
null, // SSL
host
adı
ClientName
=
"MyApp-Cache"
, // Client identifier
CommandMap
=
CommandMap.Default, // Komut mapping
DefaultDatabase
=
0
, // Varsayılan database
KeepAlive
=
60
, // Keep-alive interval
(
saniye
)
ReconnectRetryPolicy
=
new ExponentialRetry
(
1000
)
, // Reconnect stratejisi
ResolveDns
=
true, // DNS çözümleme
ServiceName
=
null, // Sentinel
service
name
TieBreaker
=
"__Booksleeve_TieBreaker"
, // Tie-breaker key
Version
=
RedisVersion.Default // Redis version
}
;
options.ConnectionMultiplexerFactory
=
async
(
)
=
>
{
var config
=
ConfigurationOptions.Parse
(
"localhost:6379"
)
;
config.ReconnectRetryPolicy
=
new ExponentialRetry
(
1000
)
;
return
await ConnectionMultiplexer.ConnectAsync
(
config
)
;
}
;
options.ProfilingSession
=
(
)
=
>
new ProfilingSession
(
)
;
}
)
;
var app
=
builder.Build
(
)
;
=
4
}
)
;
cache.Set
(
"kavanoz3"
, data3, new MemoryCacheEntryOptions
{
Size
=
3
}
)
;
// Toplam:
3
+
4
+
3
=
10
birim ✅
bash
// Toplam: 3 + 4 + 3=10 birim Doldurmuştuk.
cache.Set("kavanoz4", data4, new MemoryCacheEntryOptions { Size =2});// Toplam 12 olacak ❌
{
Size
=
1
,
AbsoluteExpirationRelativeToNow
=
TimeSpan.FromHours
(
1
)
}
)
;
//
2
. Orta Boyut Veri
(
100KB -
100
birim
)
cache.Set
(
"product-list"
, productList, new MemoryCacheEntryOptions
{
Size
=
100
,
AbsoluteExpirationRelativeToNow
=
TimeSpan.FromMinutes
(
30
)
}
)
;
//
3
. Büyük Veri
(
1MB -
1000
birim
)
cache.Set
(
"large-report"
, reportData, new MemoryCacheEntryOptions
{
Size
=
1000
,
AbsoluteExpirationRelativeToNow
=
TimeSpan.FromMinutes
(
10
)
}
)
;
//
4
. Çok Büyük Veri
(
10MB -
10000
birim
)
cache.Set
(
"database-backup"
, backupData, new MemoryCacheEntryOptions
{
Size
=
10000
,
AbsoluteExpirationRelativeToNow
=
TimeSpan.FromMinutes
(
5
)
}
)
;
"huge-data"
, hugeData
)
;
//
1
birim
(
gerçekte 10MB
)
// Her ikisi de aynı değer, ama memory kullanımları çok farklı
!
// ✅ SetSize ile
(
adil
)
cache.Set
(
"tiny-data"
, tinyData, new MemoryCacheEntryOptions
{
Size
=
1
}
)
;
cache.Set
(
"huge-data"
, hugeData, new MemoryCacheEntryOptions
{
Size
=
10000
}
)
;
// Senaryo
2
: SizeLimit
=
1000
, SetSize kullanılıyor
// Toplam
1000
birim eklenebilir
//
1
büyük entry
(
1000
birim
)
VEYA
//
1000
küçük entry
(
her biri
1
birim
)
VEYA
// Karışık boyutlarda entry
'ler
// Senaryo 3: Gerçekçi örnek
cache.Set("user-123", userData, new MemoryCacheEntryOptions { Size = 50 });
cache.Set("products", productList, new MemoryCacheEntryOptions { Size = 200 });
cache.Set("categories", categoryList, new MemoryCacheEntryOptions { Size = 100 });
cache.Set("orders", orderList, new MemoryCacheEntryOptions { Size = 500 });