Кэширование в C#: Redis, Memcached и встроенные решения для .NET
Кэширование — один из ключевых инструментов повышения производительности приложений на C#. Оно позволяет сократить время отклика, снизить нагрузку на базы данных и внешние API. В экосистеме .NET доступно несколько подходов: от встроенного MemoryCache до распределённых решений на базе Redis и Memcached. В этой статье мы разберём каждый вариант, покажем примеры кода и дадим рекомендации по выбору.
1. Встроенное кэширование: MemoryCache и IDistributedCache
Платформа .NET предоставляет два основных абстрактных механизма для работы с кэшем: IMemoryCache (in-process) и IDistributedCache (распределённый).
1.1 IMemoryCache
Хранит данные в памяти процесса. Идеально для однопоточных или монолитных приложений, где не требуется синхронизация между несколькими серверами. Прост в настройке и не требует внешних зависимостей.
// Добавление в DI (Program.cs)
builder.Services.AddMemoryCache();
// Использование в сервисе
public class WeatherService
{
private readonly IMemoryCache _cache;
public WeatherService(IMemoryCache cache)
{
_cache = cache;
}
public async Task<string> GetWeatherAsync(string city)
{
string cacheKey = $"weather_{city}";
if (_cache.TryGetValue(cacheKey, out string cachedData))
{
return cachedData;
}
// Эмуляция долгого запроса
await Task.Delay(2000);
string result = $"Погода в {city}: +25°C";
var options = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(5))
.SetSlidingExpiration(TimeSpan.FromMinutes(2));
_cache.Set(cacheKey, result, options);
return result;
}
}
1.2 IDistributedCache
Абстракция для распределённого кэша. Позволяет легко переключаться между провайдерами (Redis, SQL Server, NCache) без изменения бизнес-логики. По умолчанию хранит данные в виде byte[].
// Регистрация (Program.cs) - используем Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
// Использование
public class ProductService
{
private readonly IDistributedCache _cache;
public ProductService(IDistributedCache cache)
{
_cache = cache;
}
public async Task<Product> GetProductAsync(int id)
{
string cacheKey = $"product_{id}";
var cachedBytes = await _cache.GetAsync(cacheKey);
if (cachedBytes != null)
{
return JsonSerializer.Deserialize<Product>(cachedBytes);
}
// Запрос к БД
var product = await GetProductFromDb(id);
var options = new DistributedCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromHours(1));
var serialized = JsonSerializer.SerializeToUtf8Bytes(product);
await _cache.SetAsync(cacheKey, serialized, options);
return product;
}
}
2. Redis: распределённый кэш с продвинутыми возможностями
Redis — самое популярное решение для кэширования в .NET. Работает как высокопроизводительное in-memory хранилище с поддержкой сложных структур данных (списки, хэши, множества).
2.1 Установка и настройка
Для подключения Redis к .NET используйте пакет Microsoft.Extensions.Caching.StackExchangeRedis.
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
2.2 Пример с Redis и IDistributedCache
Помимо базового кэширования, Redis позволяет реализовать:
- Сессии пользователей — хранение данных сессии между запросами.
- Блокировки (Distributed Lock) — синхронизация доступа к ресурсам.
- Pub/Sub — обмен сообщениями между сервисами.
// Пример использования Redis для блокировки
public class OrderService
{
private readonly ConnectionMultiplexer _redis;
public OrderService(ConnectionMultiplexer redis)
{
_redis = redis;
}
public async Task<bool> TryProcessOrder(int orderId)
{
var db = _redis.GetDatabase();
string lockKey = $"lock_order_{orderId}";
string lockValue = Guid.NewGuid().ToString();
bool acquired = await db.LockTakeAsync(lockKey, lockValue, TimeSpan.FromSeconds(10));
if (!acquired) return false;
try
{
// Обработка заказа
await Task.Delay(100);
return true;
}
finally
{
await db.LockReleaseAsync(lockKey, lockValue);
}
}
}
3. Memcached: простота и скорость
Memcached — распределённая система кэширования, ориентированная на простые пары «ключ-значение». В отличие от Redis, не поддерживает сложные структуры данных и персистентность, но выигрывает в скорости на простых операциях.
3.1 Подключение Memcached к .NET
Используйте библиотеку EnyimMemcachedCore (для ASP.NET Core) или Memcached(Sharp).
dotnet add package EnyimMemcachedCore
// Program.cs
builder.Services.AddEnyimMemcached(options =>
{
options.AddServer("localhost", 11211);
});
// Использование через IDistributedCache
public class AnalyticsService
{
private readonly IDistributedCache _cache;
public AnalyticsService(IDistributedCache cache)
{
_cache = cache;
}
public async Task<int> GetPageViewsAsync(string pageUrl)
{
string cacheKey = $"views_{pageUrl}";
var cached = await _cache.GetStringAsync(cacheKey);
if (cached != null && int.TryParse(cached, out int views))
{
return views;
}
// Получаем из БД
views = await CalculateViewsFromDb(pageUrl);
await _cache.SetStringAsync(cacheKey, views.ToString(),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});
return views;
}
}
3.2 Когда выбирать Memcached?
- Требуется максимальная скорость на простых операциях Get/Set.
- Нет необходимости в персистентности или репликации.
- Объём данных невелик (до 1 МБ на ключ).
4. Сравнение и рекомендации
Выбор решения зависит от архитектуры и требований к проекту: