Кэширование в C#: Redis, Memcached и встроенные решения для .NET

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Кэширование в C#: Redis, Memcached и встроенные решения

Кэширование – один из ключевых инструментов оптимизации производительности в современных веб-приложениях. В экосистеме C# (.NET) разработчику доступны как встроенные механизмы (IMemoryCache, IDistributedCache), так и мощные внешние решения (Redis, Memcached). В этой статье мы разберем их особенности, сравним производительность и приведем практические примеры кода.

1. Встроенные решения .NET: IMemoryCache и IDistributedCache

ASP.NET Core предоставляет два основных интерфейса для работы с кэшем: IMemoryCache (локальный кэш в памяти одного процесса) и IDistributedCache (абстракция для распределенного кэша).

IMemoryCache

Идеален для монолитных приложений или микросервисов, где не требуется синхронизация между инстансами. Кэш хранится в оперативной памяти сервера.

// Регистрация в Program.cs
builder.Services.AddMemoryCache();

// Использование в сервисе
public class DataService
{
    private readonly IMemoryCache _cache;

    public DataService(IMemoryCache cache)
    {
        _cache = cache;
    }

    public async Task<string> GetExpensiveDataAsync(int id)
    {
        string cacheKey = $"data_{id}";
        if (_cache.TryGetValue(cacheKey, out string cachedData))
        {
            return cachedData;
        }

        // Эмуляция тяжелой операции
        await Task.Delay(500);
        string data = $"Result for {id}";

        var options = new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(TimeSpan.FromMinutes(10))
            .SetSlidingExpiration(TimeSpan.FromMinutes(2));

        _cache.Set(cacheKey, data, options);
        return data;
    }
}

IDistributedCache

Абстракция, позволяющая подключать различные бэкенды (Redis, SQL Server, NCache) без изменения кода приложения. Регистрируется через AddDistributedRedisCache или AddDistributedSqlServerCache.

// Пример с Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
    options.InstanceName = "SampleInstance";
});

// Использование
public class DistributedService
{
    private readonly IDistributedCache _cache;

    public DistributedService(IDistributedCache cache)
    {
        _cache = cache;
    }

    public async Task<string> GetDataAsync(string key)
    {
        var cached = await _cache.GetStringAsync(key);
        if (cached != null) return cached;

        var data = $"Computed at {DateTime.Now}";
        await _cache.SetStringAsync(key, data, new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
        });
        return data;
    }
}

2. Redis: мощный распределенный кэш для C#

Redis – это in-memory хранилище данных с поддержкой сложных структур (списки, множества, хэши). Для .NET используется клиент StackExchange.Redis или высокоуровневая обертка Microsoft.Extensions.Caching.StackExchangeRedis.

Преимущества Redis в C#:

  • Скорость: субмиллисекундные задержки (в среднем 0.5-1 мс).
  • Pub/Sub: можно оповещать другие сервисы об изменении кэша.
  • Персистентность: данные могут сохраняться на диск (RDB/AOF).
  • Кластеризация: горизонтальное масштабирование из коробки.

Пример продвинутого использования с сериализацией JSON:

using StackExchange.Redis;
using System.Text.Json;

public class RedisCacheService
{
    private readonly IDatabase _db;
    private readonly ConnectionMultiplexer _redis;

    public RedisCacheService(string connectionString)
    {
        _redis = ConnectionMultiplexer.Connect(connectionString);
        _db = _redis.GetDatabase();
    }

    public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiry = null)
    {
        var value = await _db.StringGetAsync(key);
        if (value.HasValue)
        {
            return JsonSerializer.Deserialize<T>(value);
        }

        var result = await factory();
        var serialized = JsonSerializer.Serialize(result);
        await _db.StringSetAsync(key, serialized, expiry ?? TimeSpan.FromMinutes(10));
        return result;
    }

    public async Task InvalidateAsync(string key)
    {
        await _db.KeyDeleteAsync(key);
    }
}

// Использование:
var cache = new RedisCacheService("localhost:6379");
var data = await cache.GetOrSetAsync("user:123", async () =>
{
    return await _userRepository.GetUserByIdAsync(123);
});

3. Memcached: легковесная альтернатива

Memcached – более старая, но проверенная технология распределенного кэша. Для C# существует клиент EnyimMemcached (устаревший) и современный Memcached.ClientLibrary. Однако в экосистеме .NET Memcached используется реже Redis из-за отсутствия встроенной поддержки в ASP.NET Core.

Сравнение Redis vs Memcached для C#:

Характеристика Redis Memcached
Типы данных Строки, списки, множества, хэши, битмапы Только строки (ключ-значение)
Максимальный размер значения 512 МБ 1 МБ
Персистентность Да (RDB/AOF) Нет
Pub/Sub Да Нет
Производительность (чтение) ~100,000 ops/sec (один узел) ~50,000 ops/sec
Интеграция с .NET Отличная (Microsoft.Extensions.Caching.StackExchangeRedis) Слабая (нет официального пакета от Microsoft)

Пример подключения Memcached в .NET (через NuGet пакет EnyimMemcachedCore):


                
                

Блоги

Рекомендаций по книгам