Redis Cache — StackExchange.Redis, IDistributedCache

~25 daq · .NET Backend · Ilg'or ASP.NET Core

Muammodan boshlaylik

API 3 ta server nusxasida ishlaydi (load balancer ortida). IMemoryCache bilan (67-dars):

  • Menejer mahsulot narxini o'zgartirdi — 1-serverda cache o'chirildi, 2- va 3-serverlarda eski narx 30 daqiqa ko'rinadi
  • Har bir server bazaga alohida so'rov beradi — cache samarasi 3 baravar kam
  • Deploy da server qayta ishga tushsa — cache bo'shaydi va baza birdaniga yuklanadi

Hayotiy o'xshatma

Ofisdagi har bir xodimning shaxsiy daftari (memory cache) va umumiy elektron doska (Redis). Daftardagi yozuv faqat egasiga ko'rinadi; boshqa xodim uni yangilay olmaydi. Doskaga yozilgan narsani esa hamma ko'radi, va bir kishi o'chirsa — hamma uchun o'chadi. Daftardan o'qish tezroq, lekin doska — yagona haqiqat manbai.


Redis nima?

Redis — xotirada ishlaydigan, juda tez kalit-qiymat ma'lumotlar ombori. Bir nechta server bitta Redis ga ulanadi — umumiy cache.

docker run -d --name redis -p 6379:6379 redis:7

IDistributedCache

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
builder.Services.AddStackExchangeRedisCache(o =>
{
    o.Configuration = builder.Configuration.GetConnectionString("Redis");   // "localhost:6379"
    o.InstanceName = "dokon:";                                               // kalit prefiksi
});

IDistributedCache — baytlar yoki satrlar bilan ishlaydi; ob'ektlarni o'zingiz serializatsiya qilasiz:

public class MahsulotService(DokonDbContext db, IDistributedCache cache)
{
    private static string Kalit(int id) => 
quot;mahsulot:{id}"; public async Task<MahsulotDto?> OlAsync(int id, CancellationToken ct) { var json = await cache.GetStringAsync(Kalit(id), ct); if (json is not null) return JsonSerializer.Deserialize<MahsulotDto>(json); var dto = await db.Mahsulotlar .Where(m => m.Id == id) .Select(m => new MahsulotDto(m.Id, m.Nomi, m.Narx)) .FirstOrDefaultAsync(ct); if (dto is not null) { await cache.SetStringAsync(Kalit(id), JsonSerializer.Serialize(dto), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) }, ct); } return dto; } public async Task NarxYangilaAsync(int id, decimal narx, CancellationToken ct) { await db.Mahsulotlar.Where(m => m.Id == id) .ExecuteUpdateAsync(s => s.SetProperty(m => m.Narx, narx), ct); await cache.RemoveAsync(Kalit(id), ct); // barcha serverlar uchun o'chadi } }

Bu naqsh — cache-aside: avval cache, bo'lmasa — manba va cache ga yozish; o'zgarishda — o'chirish.

Yordamchi kengaytma

public static class CacheKengaytma
{
    public static async Task<T?> OlYokiYaratAsync<T>(this IDistributedCache cache, string kalit,
        Func<Task<T?>> yaratish, TimeSpan muddat, CancellationToken ct = default)
    {
        var json = await cache.GetStringAsync(kalit, ct);
        if (json is not null) return JsonSerializer.Deserialize<T>(json);

        var qiymat = await yaratish();
        if (qiymat is not null)
            await cache.SetStringAsync(kalit, JsonSerializer.Serialize(qiymat),
                new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = muddat }, ct);
        return qiymat;
    }
}

HybridCache + Redis (.NET 9+)

AddStackExchangeRedisCache ro'yxatdan o'tgan bo'lsa, HybridCache (67-dars) Redis ni avtomatik L2 sifatida ishlatadi:

builder.Services.AddStackExchangeRedisCache(o => o.Configuration = "localhost:6379");
builder.Services.AddHybridCache(o =>
{
    o.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        Expiration = TimeSpan.FromMinutes(10),          // Redis da
        LocalCacheExpiration = TimeSpan.FromMinutes(1)  // server xotirasida
    };
});

Serializatsiya, stampede himoyasi va ikki daraja — o'zi. Yangi loyihada IDistributedCache ni qo'lda ishlatish o'rniga tavsiya etiladi.

[!INFO] Ikki darajali cache da bir server L2 dan o'chirsa ham, boshqa serverlarning L1 (xotira) nusxasi LocalCacheExpiration tugaguncha qolishi mumkin. Shuning uchun L1 muddatini qisqa qo'ying.


Redis ning boshqa imkoniyatlari

IDistributedCache faqat oddiy get/set beradi. To'liq Redis uchun — StackExchange.Redis:

builder.Services.AddSingleton<IConnectionMultiplexer>(
    ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("Redis")!));
public class KorishlarService(IConnectionMultiplexer redis)
{
    private readonly IDatabase _db = redis.GetDatabase();

    public Task<long> KorishQoshAsync(int mahsulotId) =>
        _db.StringIncrementAsync(
quot;korishlar:{mahsulotId}"); // atomik hisoblagich public Task ReytingYangilaAsync(int mahsulotId, double ball) => _db.SortedSetAddAsync("top-mahsulotlar", mahsulotId, ball); // reyting jadvali }

Hisoblagichlar, reytinglar, navbatlar, pub/sub, taqsimlangan qulf (lock), rate limit hisoblagichlari.

[!WARNING] ConnectionMultiplexer — Singleton: bitta ulanish butun ilova uchun. Har bir so'rovda yangi ulanish yaratish — Redis ni ham, ilovani ham sekinlashtiradi.


Redis ishlamay qolsa

Cache — tezlashtirish uchun, haqiqat manbai — baza. Redis o'chsa ilova to'xtamasligi kerak:

try
{
    var json = await cache.GetStringAsync(kalit, ct);
    if (json is not null) return JsonSerializer.Deserialize<MahsulotDto>(json);
}
catch (RedisConnectionException ex)
{
    log.LogWarning(ex, "Redis mavjud emas — bazadan o'qilmoqda");
}
return await BazadanOlAsync(id, ct);

Tez-tez uchraydigan xatolar

Entity ni serializatsiya qilish — navigatsiyalar bilan siklik havola; DTO.

Muddatsiz yozuvlar — Redis xotirasi to'ladi; har doim muddat va Redis da maxmemory-policy.

Ochiq Redis — parolsiz 6379 port internetga ochiq — klassik buzilish sababi; tarmoqni yoping, parol/ACL qo'ying.


Xulosa

Bir nechta server — umumiy cache kerak: Redis. AddStackExchangeRedisCache + IDistributedCache: GetStringAsync/SetStringAsync/RemoveAsync, ob'ektlar — JSON bilan; cache-aside naqshi. O'chirish — barcha serverlar uchun bir vaqtda.

.NET 9+ — HybridCache Redis ni L2 sifatida ishlatadi (tavsiya). To'liq Redis — IConnectionMultiplexer (Singleton): hisoblagichlar, sorted set lar. Redis yiqilsa — bazaga qaytish; muddat va xavfsizlik majburiy.


Keyingi dars

69-dars: CORS — frontend dan ruxsat berish, policy sozlash