Кэширование в C#: Redis, Memcached и встроенные решения — полное руководство
В современной веб-разработке производительность приложения — один из ключевых факторов успеха. Кэширование позволяет существенно снизить нагрузку на базу данных и ускорить отклик системы. В экосистеме C# (.NET) разработчику доступны три основных подхода: встроенное кэширование в памяти (IMemoryCache), распределенные решения (IDistributedCache) и внешние серверы кэширования, такие как Redis и Memcached. В этой статье мы разберем каждый из них от теории до практических примеров кода.
1. Встроенное кэширование в .NET: IMemoryCache
Встроенное кэширование — это самый простой и быстрый способ временного хранения данных. В ASP.NET Core оно реализовано через интерфейс IMemoryCache. Данные хранятся в оперативной памяти одного процесса, что обеспечивает минимальную задержку.
Когда использовать:
- Данные не нужно синхронизировать между несколькими серверами.
- Объем кэшируемых данных невелик.
- Требуется максимальная производительность без настройки внешних сервисов.
Пример реализации:
using Microsoft.Extensions.Caching.Memory;
public class WeatherService
{
private readonly IMemoryCache _cache;
public WeatherService(IMemoryCache cache)
{
_cache = cache;
}
public async Task<WeatherData> GetWeatherAsync(string city)
{
string cacheKey = $"weather_{city}";
if (!_cache.TryGetValue(cacheKey, out WeatherData data))
{
// Данных нет в кэше — загружаем из внешнего источника
data = await FetchWeatherFromApi(city);
// Настройка кэша: срок жизни 5 минут
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
_cache.Set(cacheKey, data, cacheOptions);
}
return data;
}
private async Task<WeatherData> FetchWeatherFromApi(string city)
{
// Имитация вызова внешнего API
await Task.Delay(1000);
return new WeatherData { City = city, Temperature = 22.5 };
}
}
public class WeatherData
{
public string City { get; set; }
public double Temperature { get; set; }
}
Ключевые методы: TryGetValue, Set, Remove. Важно помнить, что IMemoryCache не подходит для микросервисной архитектуры.
2. Распределенное кэширование: IDistributedCache и его провайдеры
Для приложений, работающих на нескольких серверах, необходимо распределенное кэширование. В .NET оно абстрагировано через интерфейс IDistributedCache. Это позволяет легко менять провайдер (Redis, SQL Server, NCache) без изменения бизнес-логики.
Основные методы:
GetAsync / SetAsync— для работы с байтами.GetStringAsync / SetStringAsync— для строковых данных.RefreshAsync— сброс таймера скользящего срока жизни.RemoveAsync— удаление записи.
Пример с Redis:
// В Program.cs регистрируем провайдер
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
options.InstanceName = "SampleApp";
});
// Использование в сервисе
public class ProductService
{
private readonly IDistributedCache _cache;
public ProductService(IDistributedCache cache)
{
_cache = cache;
}
public async Task<Product> GetProductAsync(int id)
{
string cacheKey = $"product_{id}";
string cachedData = await _cache.GetStringAsync(cacheKey);
if (cachedData != null)
{
return JsonSerializer.Deserialize<Product>(cachedData);
}
// Загрузка из БД
var product = await GetProductFromDatabase(id);
string serialized = JsonSerializer.Serialize(product);
var options = new DistributedCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(10));
await _cache.SetStringAsync(cacheKey, serialized, options);
return product;
}
}
3. Redis — мощный сервер кэширования
Redis — это in-memory хранилище данных, которое часто используется как кэш, брокер сообщений и база данных. В контексте C# он поддерживается через библиотеку StackExchange.Redis или через встроенный провайдер IDistributedCache.
Преимущества Redis:
- Поддержка сложных структур данных (списки, хеши, множества).
- Pub/Sub для обмена сообщениями.
- Персистентность (сохранение на диск).
- Кластеризация и высокая доступность.
Пример прямого использования 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)
{
string json = JsonSerializer.Serialize(value);
await _db.StringSetAsync(key, json, expiry);
}
public async Task<T> GetAsync<T>(string key) where T : class
{
string json = await _db.StringGetAsync(key);
return json != null ? JsonSerializer.Deserialize<T>(json) : null;
}
public async Task RemoveAsync(string key)
{
await _db.KeyDeleteAsync(key);
}
}
Типичные сценарии: кэширование сессий, ограничение частоты запросов (rate limiting), кэширование результатов тяжелых запросов.
4. Memcached — легковесная альтернатива
Memcached — это распределенная система кэширования, ориентированная на простоту и скорость. В отличие от Redis, Memcached не поддерживает сложные типы данных и персистентность, но требует меньше ресурсов.
Особенности Memcached:
- Только хранение ключ-значение (максимум 1 МБ на запись).
- Автоматическое удаление устаревших данных (LRU).
- Нет репликации (все данные хранятся на одном серве