Ответ
In-Memory Cache — это механизм хранения данных в оперативной памяти (RAM) процесса приложения для сверхбыстрого повторного доступа, позволяющий избежать повторных затратных операций (тяжёлых вычислений, запросов к БД, вызовов внешних API).
Характеристики:
- Скорость: Доступ к данным в RAM на порядки быстрее, чем к диску или сети.
- Волатильность: Данные теряются при остановке или перезапуске приложения.
- Ограниченность: Кэш конкурирует за память с самим приложением.
Типичные сценарии использования в ASP.NET Core:
- Кэширование редко меняющихся справочников из БД.
- Хранение результатов сложных вычислений или отчётов.
- Временное хранение состояния сессии пользователя.
- Кэширование ответов от внешних HTTP-сервисов.
Пример реализации с IMemoryCache:
-
Регистрация сервиса:
// В Program.cs или Startup.ConfigureServices builder.Services.AddMemoryCache(); -
Использование в сервисе:
public class CatalogService { private readonly IMemoryCache _cache; private readonly IProductRepository _repository; private readonly TimeSpan _cacheDuration = TimeSpan.FromMinutes(5); public CatalogService(IMemoryCache cache, IProductRepository repository) { _cache = cache; _repository = repository; } public async Task<List<Product>> GetTopProductsAsync() { // Попытка получить данные из кэша string cacheKey = "top_products"; if (!_cache.TryGetValue(cacheKey, out List<Product> products)) { // Данных в кэше нет -> получаем из источника (БД) products = await _repository.GetTopSellingProductsAsync(10); // Настраиваем параметры кэширования и сохраняем var cacheOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(_cacheDuration) // Удалится через 5 минут .SetPriority(CacheItemPriority.High); // При нехватке памяти удалится в последнюю очередь _cache.Set(cacheKey, products, cacheOptions); } return products; // Данные либо из кэша, либо только что загружены } }Важные аспекты:
- Стратегии вытеснения:
Absolute Expiration(удаление в заданное время),Sliding Expiration(обновление срока при каждом обращении). - Зависимости: Можно инвалидировать кэш при изменении данных, но
IMemoryCacheне предоставляет встроенного механизма для распределённых сценариев. - Распределённый кэш: Для приложений, работающих на нескольких серверах (ферма), используйте
IDistributedCache(Redis, SQL Server).
- Стратегии вытеснения:
Ответ 18+ 🔞
А, ну это же про кэш в памяти, да? Такая штука, когда ты, чтобы не ездить каждый раз в базу данных или не делать одни и те же ебанутые вычисления по сто раз, просто складываешь результат в оперативку. Дохуя быстрее, потому что память — это не диск, там всё мгновенно.
Но есть, конечно, свои подводные камни, как же без них. Во-первых, всё это добро живёт только пока твое приложение живо. Перезапустил сервер — и кэш, блядь, обнулился, всё по новой грузить надо. Во-вторых, память-то не резиновая, если начнёшь туда всё подряд пихать, приложение сожрёт всю оперативку и накроется медным тазом.
Где это обычно используют? Да везде, где можно сэкономить время. Справочники какие-нибудь, которые редко меняются. Или там отчёт, который строится полчаса — один раз посчитал, положил в кэш, и потом все юзеры им пользуются, пока он не устареет. Или состояние сессии пользователя на время его визита.
Смотри, как это в ASP.NET Core делается. Всё просто, как три копейки.
Сначала надо сервис этот самый зарегистрировать, одну строчку дописать:
builder.Services.AddMemoryCache();
А потом в любом своём сервисе ты можешь его запросить через конструктор и использовать. Вот смотри, пример сервиса, который работает с каталогом товаров.
public class CatalogService
{
private readonly IMemoryCache _cache;
private readonly IProductRepository _repository;
private readonly TimeSpan _cacheDuration = TimeSpan.FromMinutes(5);
public CatalogService(IMemoryCache cache, IProductRepository repository)
{
_cache = cache;
_repository = repository;
}
public async Task<List<Product>> GetTopProductsAsync()
{
// Пробуем вытащить из кэша, не ходить же лишний раз в базу
string cacheKey = "top_products";
if (!_cache.TryGetValue(cacheKey, out List<Product> products))
{
// А, бля, в кэше пусто! Значит, надо идти в базу, делать тяжёлую работу.
products = await _repository.GetTopSellingProductsAsync(10);
// Теперь кладём в кэш, но не навечно, а, скажем, на пять минут
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(_cacheDuration) // Через 5 минут — пиздец данным, удалятся
.SetPriority(CacheItemPriority.High); // А это чтобы при нехватке памяти удалялись в последнюю очередь
_cache.Set(cacheKey, products, cacheOptions);
}
return products; // Возвращаем либо из кэша, либо свежедобытые
}
}
Вот и вся магия. Главное — не забывать про сроки жизни данных. Можно сделать абсолютное истечение, как в примере — через пять минут нахуй, и всё. А можно sliding expiration — это когда срок сбрасывается при каждом обращении. То есть данные живут, пока к ним ходят. Перестали ходить — через заданный интервал удалились.
И ещё важный момент, чувак. Этот IMemoryCache хорош, когда у тебя приложение на одном сервере крутится. А если у тебя ферма из десяти серверов, то на каждом будет свой кэш, и они друг про друга нихуя не знают. Обновил данные на одном — на остальных девять старый хлам лежит. Для таких случаев есть IDistributedCache — это когда кэш общий на всех, типа Redis или SQL Server. Но это уже совсем другая история, с другим геморроем и скоростью.