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

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

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

Начать курс

Введение: Почему кэширование — критически важный компонент C# приложений

В современной веб-разработке производительность приложения напрямую влияет на пользовательский опыт и бизнес-показатели. Каждая секунда задержки загрузки страницы может стоить вам 7% конверсий. Кэширование в C# (.NET) — это не просто "приятная опция", а обязательный инструмент любого серьёзного разработчика.

В этой статье мы разберём три основных подхода к кэшированию в экосистеме .NET: Redis, Memcached и встроенные решения (MemoryCache). Вы узнаете, когда какой инструмент использовать, как правильно настроить кэш и избежать типовых ошибок. Материал будет полезен как начинающим C# разработчикам, так и опытным инженерам, желающим систематизировать знания.

1. Встроенное кэширование: MemoryCache и IDistributedCache

Платформа .NET предоставляет разработчикам несколько встроенных механизмов для кэширования, которые не требуют установки сторонних сервисов.

1.1 MemoryCache — локальный кэш для одного сервера

IMemoryCache — это самый простой и быстрый способ кэширования данных в C#. Данные хранятся в оперативной памяти текущего процесса. Идеально подходит для однопоточных приложений или приложений, работающих на одном сервере.

using Microsoft.Extensions.Caching.Memory;

public class ProductService
{
    private readonly IMemoryCache _cache;
    
    public ProductService(IMemoryCache cache)
    {
        _cache = cache;
    }

    public async Task<Product> GetProductAsync(int id)
    {
        string cacheKey = $"product_{id}";
        
        if (!_cache.TryGetValue(cacheKey, out Product product))
        {
            // Дорогостоящая операция (например, запрос к БД)
            product = await GetProductFromDatabaseAsync(id);
            
            var cacheOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromMinutes(30))
                .SetSlidingExpiration(TimeSpan.FromMinutes(5))
                .SetSize(1);
            
            _cache.Set(cacheKey, product, cacheOptions);
        }
        
        return product;
    }
}

Ключевые особенности MemoryCache:

  • Высокая скорость — данные читаются напрямую из RAM
  • Отсутствие сериализации — хранит объекты как есть
  • Ограничение — не работает в ферме серверов (каждый сервер имеет свой кэш)
  • Риск — при перезапуске приложения кэш полностью очищается

1.2 IDistributedCache — абстракция для распределённого кэша

Интерфейс IDistributedCache предоставляет унифицированный API для работы с любым распределённым кэшем. Вы можете переключаться между Redis, SQL Server или NCache, не меняя код бизнес-логики.

using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

public class WeatherService
{
    private readonly IDistributedCache _cache;
    
    public WeatherService(IDistributedCache cache)
    {
        _cache = cache;
    }

    public async Task<WeatherForecast> GetForecastAsync(string city)
    {
        string cacheKey = $"weather_{city}";
        string cachedData = await _cache.GetStringAsync(cacheKey);
        
        if (cachedData != null)
        {
            return JsonSerializer.Deserialize<WeatherForecast>(cachedData);
        }
        
        var forecast = await FetchFromApiAsync(city);
        var serialized = JsonSerializer.Serialize(forecast);
        
        await _cache.SetStringAsync(cacheKey, serialized, new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15)
        });
        
        return forecast;
    }
}

2. Redis — король распределённого кэша в C#

Redis (Remote Dictionary Server) — это самый популярный in-memory data store, который используется для кэширования в .NET приложениях. Он работает как отдельный сервис, к которому могут подключаться множество экземпляров вашего приложения.

2.1 Установка и настройка StackExchange.Redis

Для работы с Redis в C# используется библиотека StackExchange.Redis — де-факто стандарт в .NET сообществе.

// Установка через NuGet: Install-Package StackExchange.Redis

using StackExchange.Redis;

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

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

    public async Task SetAsync<T>(string key, T value, TimeSpan expiry)
    {
        var json = JsonSerializer.Serialize(value);
        await _db.StringSetAsync(key, json, expiry);
    }

    public async Task<T> GetAsync<T>(string key)
    {
        var value = await _db.StringGetAsync(key);
        if (value.IsNullOrEmpty) return default;
        
        return JsonSerializer.Deserialize<T>(value);
    }
}

2.2 Продвинутые возможности Redis для C#

  • Сложные структуры данных: списки, множества, хэши, сортированные множества
  • Pub/Sub: для уведомлений об изменении кэша в реальном времени
  • Locking: распределённые блокировки через RedLock
  • Персистентность: данные могут сохраняться на диск (RDB/AOF)
// Пример использования хэшей в Redis для кэширования сложных объектов
public async Task<UserProfile> GetUserProfileAsync(int userId)
{
    var hashEntries = await _db.HashGetAllAsync($"user:{userId}");
    
    if (hashEntries.Length == 0)
    {
        var profile = await LoadFromDatabase(userId);
        
        var entries = new HashEntry[]
        {
            new("name", profile.Name),
            new("email", profile.Email),
            new("age", profile.Age.ToString())
        };
        
        await _db.HashSetAsync($"user:{userId}", entries);
        await _db.KeyExpireAsync($"user:{userId}", TimeSpan.FromHours(1));
        
        return profile;
    }
    
    return new UserProfile
    {
        Name = hashEntries.FirstOrDefault(x => x.Name == "name").Value,
        Email = hashEntries.FirstOrDefault(x => x.Name == "email").Value,
        Age = int.Parse(hashEntries.FirstOrDefault(x => x.Name == "age").Value)
    };
}

3. Memcached — лёгкий и быстрый распределённый кэш

Memcached — это высокопроизводительная распределённая система кэширования. В отличие от Redis, она не поддерживает сложные структуры данных и персистентность, но зато

Блоги

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