Як реалізувати стратегії кешування в NestJS?
Кешування в NestJS
NestJS надає вбудований CacheModule, який підтримує кілька сховищ кешу: в пам'яті, Redis, Memcached та інші.
Налаштування
bash
npm install @nestjs/cache-manager cache-manager
npm install cache-manager-redis-yet redis # для RedisКеш в пам'яті
typescript
import { CacheModule } from '@nestjs/cache-manager';
@Module({
imports: [
CacheModule.register({
ttl: 60000, // 60 секунд (в мс)
max: 100, // максимальна кількість елементів у кеші
}),
],
})
export class AppModule {}Redis Кеш
typescript
import { CacheModule } from '@nestjs/cache-manager';
import { redisStore } from 'cache-manager-redis-yet';
@Module({
imports: [
CacheModule.registerAsync({
useFactory: async () => ({
store: await redisStore({
socket: { host: 'localhost', port: 6379 },
ttl: 60000,
}),
}),
}),
],
})
export class AppModule {}Використання CacheInterceptor (Автоматичне кешування)
typescript
import { CacheInterceptor, CacheTTL, CacheKey } from '@nestjs/cache-manager';
@Controller('products')
@UseInterceptors(CacheInterceptor) // Кешувати всі GET ендпоїнти
export class ProductsController {
@Get()
@CacheTTL(30000) // Перезаписати TTL — 30 секунд
findAll() {
return this.productsService.findAll();
}
@Get(':id')
@CacheKey('product-detail') // Користувацький ключ кешу
@CacheTTL(60000)
findOne(@Param('id') id: string) {
return this.productsService.findOne(id);
}
}Ручне управління кешем
typescript
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
@Injectable()
export class ProductsService {
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
async findOne(id: string) {
// Спочатку пробуємо кеш
const cached = await this.cacheManager.get(`product:${id}`);
if (cached) return cached;
// Отримуємо з БД
const product = await this.productRepo.findById(id);
// Зберігаємо в кеш
await this.cacheManager.set(`product:${id}`, product, 60000);
return product;
}
async update(id: string, data: UpdateProductDto) {
const product = await this.productRepo.update(id, data);
// Інвалідовуємо кеш
await this.cacheManager.del(`product:${id}`);
await this.cacheManager.del('products:list');
return product;
}
async clearAll() {
await this.cacheManager.reset();
}
}Шаблон Cache-Aside
typescript
async getOrSet<T>(key: string, factory: () => Promise<T>, ttl?: number): Promise<T> {
const cached = await this.cacheManager.get<T>(key);
if (cached !== undefined && cached !== null) {
return cached;
}
const value = await factory();
await this.cacheManager.set(key, value, ttl);
return value;
}
// Використання
const product = await this.getOrSet(
`product:${id}`,
() => this.productRepo.findById(id),
60000,
);Стратегії інвалідизації кешу
| Стратегія | Коли використовувати |
|---|---|
| На основі TTL | Дані змінюються рідко |
| Write-through | Інвалідовувати/оновлювати кеш при кожному записі |
| На основі подій | Слухати події змін |
| На основі шаблону | Очищати всі ключі, що відповідають шаблону |
Резюме
| Підхід | Плюси | Мінуси |
|---|---|---|
| CacheInterceptor | Нульовий код, автоматично | Менше контролю |
| Ручний (CACHE_MANAGER) | Повний контроль | Більше коду |
| Допоміжник Cache-Aside | Повторне використання, чисто | Потрібно створити |
Порада для виробництва: Використовуйте Redis для кешування в продукції. Встановлюйте відповідні TTL на основі волатильності даних. Завжди інвалідизуйте кеш при записах. Моніторте співвідношення попадань/промахів кешу для оптимізації TTL.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.