Введение: Почему кэширование критически важно для TypeScript-приложений
Современные веб-приложения на TypeScript сталкиваются с растущими нагрузками и требованиями к скорости ответа. Кэширование — это не просто опция, а необходимость для любого production-grade приложения. Без него каждый запрос к базе данных или внешнему API превращается в дорогостоящую операцию, увеличивающую время отклика в 10-100 раз.
В этой статье мы разберем три основных подхода к кэшированию в TypeScript: Redis — распределенный кэш, Memcached — легковесная альтернатива и встроенные решения (in-memory cache). Вы узнаете, как выбрать подходящий инструмент и реализовать его с примерами кода.
1. Встроенное кэширование: In-Memory Cache в TypeScript
Самый простой и быстрый способ — хранить данные прямо в оперативной памяти процесса. Для TypeScript/Node.js это идеально подходит для монолитных приложений или микросервисов с одним инстансом.
Реализация простого in-memory кэша
Создадим класс SimpleCache с поддержкой TTL (время жизни):
interface CacheEntry<T> {
value: T;
expiresAt: number;
}
class SimpleCache<T> {
private store = new Map<string, CacheEntry<T>>();
private defaultTTL: number; // в миллисекундах
constructor(defaultTTL: number = 60000) {
this.defaultTTL = defaultTTL;
}
set(key: string, value: T, ttl?: number): void {
const expiresAt = Date.now() + (ttl ?? this.defaultTTL);
this.store.set(key, { value, expiresAt });
}
get(key: string): T | null {
const entry = this.store.get(key);
if (!entry) return null;
if (Date.now() > entry.expiresAt) {
this.store.delete(key);
return null;
}
return entry.value;
}
has(key: string): boolean {
return this.get(key) !== null;
}
delete(key: string): void {
this.store.delete(key);
}
clear(): void {
this.store.clear();
}
}
// Пример использования
const userCache = new SimpleCache<User>(30000); // TTL 30 секунд
userCache.set('user:123', { id: 123, name: 'Анна' });
const cachedUser = userCache.get('user:123');
Плюсы и минусы in-memory кэша
- Плюсы: максимальная скорость (наносекунды), нет сетевых задержек, простота реализации.
- Минусы: данные теряются при перезапуске приложения, ограничены объемом RAM одного процесса, не работают при горизонтальном масштабировании.
Когда использовать: для кэширования результатов тяжелых вычислений, конфигураций, статических данных, которые редко меняются.
2. Redis: Распределенное кэширование для TypeScript
Redis — это самый популярный in-memory data store, который используется как кэш, брокер сообщений и база данных. Для TypeScript мы используем библиотеку ioredis.
Установка и базовая настройка
npm install ioredis
npm install @types/ioredis -D
Создадим сервис для работы с Redis:
import Redis from 'ioredis';
class RedisCacheService {
private client: Redis;
private defaultTTL: number;
constructor(ttlSeconds: number = 3600) {
this.client = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: Number(process.env.REDIS_PORT) || 6379,
retryStrategy: (times) => Math.min(times * 50, 2000),
});
this.defaultTTL = ttlSeconds;
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
const serialized = JSON.stringify(value);
const ttlToUse = ttl ?? this.defaultTTL;
await this.client.setex(key, ttlToUse, serialized);
}
async get<T>(key: string): Promise<T | null> {
const data = await this.client.get(key);
if (!data) return null;
try {
return JSON.parse(data) as T;
} catch {
return null;
}
}
async delete(key: string): Promise<void> {
await this.client.del(key);
}
async flushAll(): Promise<void> {
await this.client.flushall();
}
}
// Пример с кэшированием данных из API
async function getUserData(userId: number): Promise<User> {
const cacheKey = `user:${userId}`;
const cached = await cacheService.get<User>(cacheKey);
if (cached) return cached;
const user = await fetchUserFromDB(userId);
await cacheService.set(cacheKey, user);
return user;
}
Продвинутые возможности Redis
- Кэширование с инвалидацией по шаблону — удаление всех ключей по паттерну
user:* - Использование Redis Sets и Sorted Sets для кэширования списков с сортировкой
- Pub/Sub для синхронизации кэша между разными инстансами
Когда использовать: распределенные системы, микросервисы, приложения с высокой нагрузкой, где требуется общий кэш между несколькими серверами.
3. Memcached: Легковесная альтернатива Redis
Memcached — это еще один популярный распределенный кэш, который проще Redis, но имеет свои особенности. Для TypeScript используем библиотеку memcached.
Реализация клиента Memcached
npm install memcached
npm install @types/memcached -D
import Memcached from 'memcached';
class MemcachedCacheService {
private client: Memcached;
private defaultTTL: number;
constructor(serverLocation: string = 'localhost:11211', ttl: number = 3600) {
this.client = new Memcached(serverLocation);
this.defaultTTL = ttl;
}
async set<T>(key: string, value: T, ttl?: number): Promise<boolean> {
return new Promise((resolve, reject) => {
const ttlToUse = ttl ?? this.defaultTTL;
this.client.set(key, value, ttlToUse, (err) => {
if (err) reject(err);
else resolve(true);
});
});
}
async get<T>(key: string): Promise<T | null> {
return new Promise((resolve, reject) =>