Кэширование в TypeScript: Redis, Memcached и встроенные решения

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему кэширование критически важно для 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) =>                

Блоги

Рекомендаций по книгам