← Назад к вопросам

Какие знаешь виды кэша?

2.0 Middle🔥 171 комментариев
#Кэширование и Redis

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Виды кэша в Backend-разработке на C#

В контексте backend-разработки на C# и распределённых систем, кэширование — это ключевая техника для повышения производительности, снижения нагрузки на базы данных и улучшения отзывчивости приложений. Существует несколько классификаций видов кэша, которые можно разделить по месту расположения, стратегии обновления и архитектуре.

1. По месту расположения (уровню в приложении)

Кэш на клиентской стороне (Client-Side Cache)

Располагается непосредственно в браузере или мобильном приложении. Часто используется для статических ресурсов (CSS, JS, изображения) через HTTP-заголовки (Cache-Control, ETag). В C# backend это контролируется middleware в ASP.NET Core:

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=600");
    }
});

Кэш на уровне веб-сервера (Web Server Cache)

Кэширование целых HTTP-ответов или фрагментов HTML. В ASP.NET Core реализуется через Response Caching Middleware:

app.UseResponseCaching();
services.AddResponseCaching();

А также через атрибут [ResponseCache] на контроллерах или действиях.

Кэш уровня приложения (In-Memory Cache)

Хранится в оперативной памяти процесса приложения. В C# это IMemoryCache, который предоставляет простой ключ-значение хранилище:

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

    public async Task<Product> GetProductAsync(int id)
    {
        return await _cache.GetOrCreateAsync($"product_{id}", async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
            return await _dbContext.Products.FindAsync(id);
        });
    }
}

Плюсы: Минимальная задержка (наносекунды).
Минусы: Ограничен объёмом памяти, данные теряются при перезапуске приложения, не разделяются между экземплярами (в кластере).

Распределённый кэш (Distributed Cache)

Хранится вне процесса приложения, обычно в специализированном хранилище, доступном для всех узлов кластера. В C# используется IDistributedCache с поддержкой различных бэкендов:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
});

Пример использования:

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

    var data = await FetchDataFromDbAsync();
    await _distributedCache.SetStringAsync(key, data, new DistributedCacheEntryOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
    });
    return data;
}

Популярные решения: Redis (наиболее часто в .NET-экосистеме), Memcached, NCache.
Преимущества: Согласованность данных между узлами, устойчивость к перезапускам, горизонтальная масштабируемость.
Недостатки: Сетевая задержка (миллисекунды), сложность настройки и поддержки инфраструктуры.

Кэш базы данных (Database Cache)

Встроенные механизмы СУБД, такие как кэш запросов в SQL Server или Redis для PostgreSQL. Часто настраивается на стороне БД, но требует внимания к инвалидации.

2. По стратегии обновления (записи)

  • Кэш с отложенной записью (Cache-Aside / Lazy Loading): Приложение явно управляет кэшем. При промахе (cache miss) данные загружаются из источника, затем помещаются в кэш. Наиболее распространённая стратегия в C# (примеры выше).
  • Сквозная запись (Write-Through): Данные записываются одновременно в кэш и в источник. Обеспечивает согласованность, но увеличивает задержку записи.
  • Запись с задержкой (Write-Behind / Write-Back): Данные сначала записываются в кэш, а затем асинхронно переносятся в источник. Высокая производительность записи, но риск потери данных при сбое.
  • Насильственное обновление (Refresh-Ahead): Кэш автоматически обновляет данные до истечения срока их жизни, если к ним есть активный доступ.

3. По архитектуре и назначению

  • Кэш второго уровня (L2 Cache): В ORM, например, Entity Framework Core не имеет встроенного L2 кэша, но NHibernate и некоторые расширения EF Core добавляют его для кэширования результатов запросов или сущностей.
  • Гибридный кэш: Комбинация in-memory и распределённого кэша. Например, «Two-level cache»: первый уровень — быстрый IMemoryCache на каждом узле, второй — общий IDistributedCache (Redis) для синхронизации. Реализуется через библиотеки, такие как EasyCaching.
  • Специализированные кэши:
    *   **Кэш сессий (Session Cache)**: Хранение данных сессии пользователя. В ASP.NET Core по умолчанию используется in-memory, но для кластера настраивается на распределённый (Redis).
```csharp
services.AddSession(options => {...});
services.AddDistributedRedisCache(...); // Для распределённых сессий
```
    *   **Кэш для защиты от перегрузки (Rate Limiting Cache)**: Хранение счётчиков запросов для API. Реализуется через `Microsoft.Extensions.Caching.Distributed`.

Критерии выбора в проекте на C#

  • Требования к задержке: Для наносекунд — in-memory; для миллисекунд и кластера — распределённый.
  • Объём данных: Большие объёмы (> нескольких ГБ) — Redis или Memcached.
  • Согласованность данных: Высокая важность — распределённый кэш с правильной стратегией инвалидации.
  • Сложность инфраструктуры: In-memory проще, но Redis требует отдельного сервера/кластера.
  • Бюджет: Redis как managed-сервис (Azure Cache, AWS ElastiCache) увеличивает стоимость, но снижает операционные затраты.

В современных .NET-приложениях часто применяется комбинированный подход: IMemoryCache для быстро меняющихся или локальных данных, Redis через IDistributedCache для общих данных сессий, каталогов продуктов или результатов тяжёлых запросов, и HTTP-кэширование для статики или публичных API. Правильная стратегия инвалидации (по времени, по событиям, ручная) так же важна, как и выбор типа кэша.