DevOps & Cloud

Redis ile Cache Yönetimi: .NET Core ve Performans Optimizasyonu

11 dk okuma16 Mart 2026
Redis cacheRedis .NETDistributed cacheRedis TürkiyeCache yönetimiRedis pub/subSignalR Redis backplanePerformans optimizasyonuRedis sessionIn-memory cache

Redis, yuksek performansli bir in-memory veri yapisi deposu olup distributed cache, session yonetimi, mesaj kuyrugu ve real-time uygulamalar icin vazgecilmez bir aractir. .NET ekosisteminde IDistributedCache arayuzu ile sorunsuz entegrasyon saglar ve SignalR backplane olarak coklu sunucu ortamlarinda real-time iletisimi mumkun kilar. Bu yazida Redis cache stratejilerini, .NET entegrasyonunu ve production ortamlarindaki best practice'leri detayli olarak inceleyecegim.

Projelerimde Redis kullanarak API response surelerini %80'e kadar dusurdugumuz ve veritabani yukunu onemli olcude azalttigimizi deneyimledim. Dogru cache stratejisi, uygulamanizin performansini dramatik sekilde artirir.

Redis .NET Entegrasyonu

IDistributedCache ile Temel Kullanim

.NET'in IDistributedCache arayuzu, cache provider'dan bagimsiz bir soyutlama saglar. Redis'e gecis yapmak icin sadece kayit degisikligi yeterlidir.

csharp
// Program.cs - Redis cache kayit
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "MyApp:";
});

// Servis katmaninda cache kullanimi
public class ProductService
{
    private readonly IDistributedCache _cache;
    private readonly ApplicationDbContext _context;
    private static readonly DistributedCacheEntryOptions CacheOptions = new()
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
        SlidingExpiration = TimeSpan.FromMinutes(10)
    };

    public ProductService(IDistributedCache cache, ApplicationDbContext context)
    {
        _cache = cache;
        _context = context;
    }

    public async Task<ProductDto?> GetProductAsync(int id, CancellationToken ct)
    {
        var cacheKey = $"product:{id}";

        // Cache'den okuma
        var cached = await _cache.GetStringAsync(cacheKey, ct);
        if (cached is not null)
            return JsonSerializer.Deserialize<ProductDto>(cached);

        // Veritabanindan cek ve cache'e yaz
        var product = await _context.Products
            .Where(p => p.Id == id)
            .Select(p => new ProductDto(p.Id, p.Name, p.Price))
            .FirstOrDefaultAsync(ct);

        if (product is not null)
        {
            var json = JsonSerializer.Serialize(product);
            await _cache.SetStringAsync(cacheKey, json, CacheOptions, ct);
        }

        return product;
    }

    public async Task InvalidateProductCacheAsync(int id, CancellationToken ct)
    {
        await _cache.RemoveAsync($"product:{id}", ct);
    }
}

StackExchange.Redis ile Ileri Seviye Islemler

IDistributedCache basit senaryolar icin yeterli olsa da, Redis'in tum gucunden yararlanmak icin StackExchange.Redis kutuphanesini dogrudan kullanabilirsiniz:

csharp
// Redis connection multiplexer kayit
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
{
    var config = ConfigurationOptions.Parse(
        builder.Configuration.GetConnectionString("Redis")!);
    config.AbortOnConnectFail = false;
    config.ConnectRetry = 3;
    config.ConnectTimeout = 5000;
    config.SyncTimeout = 3000;
    return ConnectionMultiplexer.Connect(config);
});

// Ileri seviye Redis operasyonlari
public class RedisCacheService
{
    private readonly IDatabase _db;

    public RedisCacheService(IConnectionMultiplexer redis)
    {
        _db = redis.GetDatabase();
    }

    // Sorted Set ile leaderboard
    public async Task UpdateLeaderboardAsync(string userId, double score)
    {
        await _db.SortedSetAddAsync("leaderboard", userId, score);
    }

    public async Task<List<LeaderboardEntry>> GetTopPlayersAsync(int count)
    {
        var entries = await _db.SortedSetRangeByRankWithScoresAsync(
            "leaderboard", 0, count - 1, Order.Descending);

        return entries.Select(e => new LeaderboardEntry(
            e.Element.ToString(), e.Score)).ToList();
    }

    // Hash ile kullanici oturumu
    public async Task SetUserSessionAsync(string sessionId, UserSession session)
    {
        var hashEntries = new HashEntry[]
        {
            new("userId", session.UserId),
            new("email", session.Email),
            new("role", session.Role),
            new("loginAt", session.LoginAt.ToString("O"))
        };

        await _db.HashSetAsync($"session:{sessionId}", hashEntries);
        await _db.KeyExpireAsync($"session:{sessionId}", TimeSpan.FromHours(24));
    }

    // Distributed lock
    public async Task<bool> AcquireLockAsync(string resource, TimeSpan expiry)
    {
        return await _db.StringSetAsync(
            $"lock:{resource}", Environment.MachineName, expiry, When.NotExists);
    }
}

SignalR Redis Backplane

Coklu Sunucu Ortaminda Real-Time Iletisim

Uygulamaniz birden fazla sunucuda calistiginda, SignalR mesajlarinin tum sunuculara iletilmesi gerekir. Redis backplane bu sorunu cozer.

csharp
// Program.cs - SignalR Redis backplane yapilandirmasi
builder.Services.AddSignalR()
    .AddStackExchangeRedis(builder.Configuration.GetConnectionString("Redis")!, options =>
    {
        options.Configuration.ChannelPrefix = RedisChannel.Literal("MyApp");
    });

// SignalR Hub
public class NotificationHub : Hub
{
    public async Task SendToGroup(string groupName, string message)
    {
        // Bu mesaj Redis uzerinden tum sunuculara iletilir
        await Clients.Group(groupName).SendAsync("ReceiveNotification", message);
    }

    public async Task JoinGroup(string groupName)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
    }
}

Cache Stratejileri

Cache-Aside (Lazy Loading)

En yaygin stratejidir. Veri oncelikle cache'den okunur, bulunamazsa kaynaktan cekilir ve cache'e yazilir. Yukaridaki ProductService ornegi bu pattern'i kullanir.

Write-Through

Veri yazma islemlerinde hem veritabani hem de cache esanli olarak guncellenir. Veri tutarliligi kritik olan senaryolarda tercih edilir.

Cache Invalidation Stratejileri

Cache invalidation, distributed sistemlerde en zorlu problemlerden biridir. Yanlis invalidation, stale data veya gereksiz cache miss'lere yol acar:

  • TTL (Time to Live): Otomatik suresi dolma ile basit invalidation
  • Event-based: Veri degistiginde aktif olarak cache'i temizleme
  • Tag-based: Iliskili cache kayitlarini gruplandirarak toplu temizleme
  • Pattern-based: Key pattern'ine gore toplu silme
csharp
// Pattern-based cache invalidation ornegi
public class CacheInvalidationService
{
    private readonly IConnectionMultiplexer _redis;

    public CacheInvalidationService(IConnectionMultiplexer redis)
    {
        _redis = redis;
    }

    // Belirli bir kategorideki tum urunlerin cache'ini temizle
    public async Task InvalidateCategoryProductsAsync(int categoryId)
    {
        var server = _redis.GetServer(_redis.GetEndPoints().First());
        var db = _redis.GetDatabase();

        var keys = server.Keys(pattern: $"MyApp:product:category:{categoryId}:*");
        foreach (var key in keys)
        {
            await db.KeyDeleteAsync(key);
        }
    }

    // Write-through pattern ile cache ve DB'yi esanli guncelle
    public async Task UpdateProductWithCacheAsync(
        int id, ProductUpdateDto dto, ApplicationDbContext context)
    {
        var db = _redis.GetDatabase();

        // Veritabanini guncelle
        var product = await context.Products.FindAsync(id);
        if (product is null) return;

        product.Name = dto.Name;
        product.Price = dto.Price;
        await context.SaveChangesAsync();

        // Cache'i guncelle (write-through)
        var cacheKey = $"MyApp:product:{id}";
        var json = JsonSerializer.Serialize(new ProductDto(id, dto.Name, dto.Price));
        await db.StringSetAsync(cacheKey, json, TimeSpan.FromMinutes(30));
    }
}

Redis Streams ile Event-Driven Mimari

Redis Streams, Kafka benzeri bir mesajlasma yapisini Redis uzerinde kurmanizi saglar. Consumer group'lar ile birden fazla tuketici arasinda mesaj dagitimi yapilabilir:

csharp
// Redis Streams ile mesaj gonderme
public async Task PublishOrderEventAsync(OrderEvent orderEvent)
{
    var db = _redis.GetDatabase();
    var entries = new NameValueEntry[]
    {
        new("type", orderEvent.Type),
        new("orderId", orderEvent.OrderId.ToString()),
        new("data", JsonSerializer.Serialize(orderEvent)),
        new("timestamp", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString())
    };

    await db.StreamAddAsync("order-events", entries, maxLength: 10000);
}

// Consumer group ile mesaj okuma
public async Task ConsumeOrderEventsAsync(CancellationToken ct)
{
    var db = _redis.GetDatabase();
    var groupName = "order-processor";
    var consumerName = Environment.MachineName;

    // Consumer group olustur (yoksa)
    try { await db.StreamCreateConsumerGroupAsync("order-events", groupName, "0"); }
    catch (RedisServerException) { /* Grup zaten var */ }

    while (!ct.IsCancellationRequested)
    {
        var entries = await db.StreamReadGroupAsync(
            "order-events", groupName, consumerName, ">", count: 10);

        foreach (var entry in entries)
        {
            // Mesaji isle
            var data = entry.Values.First(v => v.Name == "data").Value;
            await ProcessOrderEventAsync(data.ToString());

            // Mesaji onayla (acknowledge)
            await db.StreamAcknowledgeAsync("order-events", groupName, entry.Id);
        }

        await Task.Delay(100, ct);
    }
}

Redis Izleme ve Performans Analizi

redis-cli ile Monitoring

Production ortaminda Redis performansini izlemek icin redis-cli guclu bir aractir:

bash
# Canli komut izleme (dikkat: production'da performans etkisi olabilir)
redis-cli MONITOR

# Redis sunucu istatistikleri
redis-cli INFO stats

# Bellek kullanim detaylari
redis-cli INFO memory

# Bagli istemcileri listele
redis-cli CLIENT LIST

# Yavas sorgulari logla (10ms'den yavas)
redis-cli CONFIG SET slowlog-log-slower-than 10000
redis-cli SLOWLOG GET 10

# Key sayisi ve boyut analizi
redis-cli DBSIZE
redis-cli INFO keyspace

# Belirli bir key pattern'inin bellek kullanimini analiz et
redis-cli --bigkeys

Bellek Optimizasyonu

Redis bellek kullanimimini optimize etmek icin su stratejileri uygulayabilirsiniz:

  • maxmemory-policy: allkeys-lru en yaygin tercih olup, bellek dolunca en az erisilen key'leri siler
  • Key expiration: Her key icin uygun TTL belirleyin, suresi belirsiz key'lerden kacinin
  • Veri sıkistirma: Buyuk JSON degerlerini LZ4 veya Gzip ile sikistirarak depolama alanini azaltin
  • Hash yerine String: Az alanli kucuk nesneler icin hash veri yapisi bellek acisindan daha verimlidir
  • Key isimlendirme: Kisa ama anlamli key isimleri kullanarak bellek israfini onleyin

Redis Yapilandirma Onerileri

Production ortami icin onemli ayarlar:

  • maxmemory-policy: allkeys-lru veya volatile-lru ile bellek yonetimi
  • appendonly: Veri kaliciligi icin AOF (Append Only File) etkinlestirme
  • Sentinel/Cluster: Yuksek erisilebilirlik icin Redis Sentinel veya Cluster modu
  • Connection pooling: ConnectionMultiplexer'i singleton olarak kullanin, her istekte yeni baglanti acmayin

Pratik Oneriler ve Ozet

Redis cache yonetiminde su noktalara dikkat edin:

  1. Cache key tasarimi: Tutarli ve anlamli key isimlendirmesi kullanin (orn. entity:id:field)
  2. TTL stratejisi: Her veri tipi icin uygun suresi dolma suresi belirleyin
  3. Monitoring: Redis INFO komutu ve RedisInsight ile performansi izleyin
  4. Bellek yonetimi: maxmemory ayarini sunucu RAM'inin %70'inden fazla yapmayin
  5. Serialization: System.Text.Json ile hizli ve verimli serialization kullanin

Redis, dogru kullanildiginda uygulamanizin performansini katbekat artirir. Onemli olan, her senaryo icin dogru veri yapisini ve cache stratejisini secmektir.

İlgili Makaleler

Flutter Projeniz mi Var?

iOS, Android ve web için yüksek performanslı Flutter uygulamaları geliştiriyorum.

İletişime Geç