Ответ
Кэширование — ключевая техника для повышения производительности. Виды кэширования классифицируют по разным критериям:
1. По расположению (уровень в архитектуре)
| Уровень | Технологии/Примеры | Когда использовать |
|---|---|---|
| Клиентский | HTTP-заголовки (Cache-Control, ETag), LocalStorage, Service Worker |
Статичные ресурсы (CSS, JS, изображения), данные, не меняющиеся часто. |
| Сетевой (CDN) | Cloudflare, AWS CloudFront, Akamai | Глобальное распространение статического контента и даже динамического (с edge computing). |
| Веб-сервер/Прокси | NGINX, Varnish, IIS Output Caching | Кэширование целых HTML-страниц или API-ответов. |
| Уровень приложения (In-Memory) | IMemoryCache (ASP.NET Core), ConcurrentDictionary, статические поля |
Данные, общие для всех пользователей в рамках одного экземпляра приложения (справочники, конфигурация). |
| Распределенный (Distributed) | Redis, Memcached, NCache, SQL Server как кэш | Данные, которые должны быть доступны всем экземплярам масштабируемого приложения (сессии, результаты тяжелых запросов). |
| Уровень базы данных | Query cache (MySQL), Buffer pool (SQL Server), Materialized Views | Внутренний кэш СУБД для ускорения повторяющихся запросов. |
2. По стратегии записи (Write Policy)
- Write-Through: Данные записываются одновременно в кэш и в основное хранилище (БД). Гарантирует согласованность, но медленнее на запись.
- Write-Back (Write-Behind): Данные сначала пишутся в кэш, а в БД записываются асинхронно пачкой. Высокая производительность на запись, но риск потери данных при сбое.
- Write-Around: Запись идет напрямую в БД, минуя кэш. Кэш обновляется только при последующем чтении. Подходит для данных, которые редко перечитываются после записи.
3. По стратегии вытеснения (Eviction Policy) При заполнении кэша нужно решать, какие данные удалить:
- LRU (Least Recently Used): Удаляет давно неиспользуемые. Самый популярный и эффективный в большинстве случаев.
- LFU (Least Frequently Used): Удаляет реже всего используемые.
- FIFO (First In, First Out): Удаляет самые старые по времени добавления.
- Sliding Expiration: Данные удаляются, если к ним не обращались в течение заданного интервала.
- Absolute Expiration: Данные удаляются в определенный момент времени.
4. По типу кэшируемого контента
- Кэширование объектов: Сохранение десериализованных объектов (например,
User,Product). - Кэширование данных: Сохранение результатов запросов к БД (сырых данных или DTO).
- Кэширование вычислений: Сохранение результатов тяжелых вычислений (например, агрегаций, отчетов).
- Кэширование страниц (Output Caching): Сохранение готового HTML.
Практический пример в ASP.NET Core:
// 1. In-Memory Cache (для данных в рамках одного сервера)
public class CatalogService
{
private readonly IMemoryCache _cache;
public CatalogService(IMemoryCache cache) => _cache = cache;
public async Task<List<Product>> GetProductsAsync()
{
// Пытаемся получить из кэша
if (!_cache.TryGetValue("TopProducts", out List<Product> products))
{
// Кэш пуст — идем в БД
products = await _dbContext.Products.Take(10).ToListAsync();
// Сохраняем в кэш на 5 минут
_cache.Set("TopProducts", products, TimeSpan.FromMinutes(5));
}
return products;
}
}
// 2. Распределенный кэш (Redis)
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
// Использование IDistributedCache аналогично IMemoryCache
Критические аспекты при работе с кэшем:
- Инвалидация: Самый сложный момент. Как и когда очищать устаревшие данные? Используйте TTL (время жизни), зависимые тэги или паттерн «Cache Aside».
- Согласованность: Кэш и БД могут расходиться. Выберите подходящую стратегию записи для ваших требований (возможна eventual consistency).
- Сериализация: Для распределенного кэша объекты должны сериализоваться (JSON, MessagePack, Protobuf).
- Горячие ключи (Hot Keys): Один часто запрашиваемый ключ может создать нагрузку на один узел Redis. Решение — шардирование или дублирование ключей.