Кэширование в JavaScript: Redis, Memcached и встроенные решения: от А до Я
В современной веб-разработке на Node.js производительность приложения напрямую зависит от скорости доступа к данным. Каждый запрос к базе данных, внешнему API или файловой системе создаёт задержку, которая накапливается при росте нагрузки. Кэширование — это стратегия временного хранения часто запрашиваемых данных в быстром хранилище, позволяющая сократить время ответа в 10–100 раз. В этой статье мы разберём три подхода к кэшированию в JavaScript: встроенные решения (Map, WeakMap, модуль node-cache), распределённые системы Redis и Memcached, а также их комбинации.
Зачем нужно кэширование в Node.js?
Основная цель кэширования — снизить нагрузку на медленные ресурсы (базы данных, диски, внешние сервисы) и ускорить отдачу данных пользователю. В типичном веб-приложении на Node.js кэш может хранить:
- Результаты сложных запросов к БД (агрегации, JOIN).
- Ответы от внешних API (погода, курсы валют, геоданные).
- Сессии пользователей и токены аутентификации.
- HTML-фрагменты или целые страницы (SSR кэширование).
- Конфигурационные файлы и шаблоны.
Без кэширования каждое повторное обращение к одним и тем же данным приводит к дублированию работы. С кэшем — данные извлекаются из оперативной памяти за микросекунды.
Встроенные решения для кэширования в JavaScript
Для простых сценариев или однопоточных приложений можно использовать нативные структуры данных JavaScript. Они не требуют установки внешних пакетов, но имеют ограничения по объёму и времени жизни.
1. Map и WeakMap
Объект Map — это коллекция ключ-значение, идеально подходящая для кэша в рамках одного процесса. Пример простого кэша с TTL (time-to-live):
class SimpleCache {
constructor(ttl = 60000) {
this.cache = new Map();
this.ttl = ttl;
}
set(key, value) {
this.cache.set(key, {
value,
expiry: Date.now() + this.ttl
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() > item.expiry) {
this.cache.delete(key);
return null;
}
return item.value;
}
has(key) {
return this.get(key) !== null;
}
}
// Использование
const cache = new SimpleCache(5000); // TTL 5 секунд
cache.set('user:123', { name: 'Иван' });
console.log(cache.get('user:123')); // { name: 'Иван' }
WeakMap отличается тем, что его ключи — только объекты, и сборщик мусора может удалять записи, если на объект-ключ больше нет ссылок. Это удобно для кэширования DOM-элементов или временных объектов.
2. Модуль node-cache
Для production-среды лучше использовать готовую библиотеку node-cache. Она поддерживает TTL, автоматическую очистку и работает быстрее самописного решения:
const NodeCache = require('node-cache');
const myCache = new NodeCache({ stdTTL: 100, checkperiod: 120 });
// Запись
myCache.set('key', { data: 'value' });
// Чтение
const value = myCache.get('key');
if (value === undefined) {
// Кэш промах — нужно получить данные из БД
}
// Удаление
myCache.del('key');
Встроенные решения подходят для монолитных приложений или микросервисов с одним экземпляром. Однако при масштабировании на несколько процессов или серверов данные становятся несогласованными — здесь нужны распределённые кэши.
Redis: мощный in-memory data store
Redis — это высокопроизводительное хранилище данных в оперативной памяти, поддерживающее строки, хэши, списки, множества и другие структуры. Для Node.js используется пакет redis или ioredis. Redis идеален для кэширования благодаря:
- Поддержке TTL на уровне ключа (команда
EXPIRE). - Атомарным операциям (INCR, DECR, SETNX).
- Встроенной репликации и кластеризации.
- Возможности сохранять данные на диск (RDB/AOF).
Пример кэширования с Redis в Node.js
const Redis = require('ioredis');
const redis = new Redis({
host: 'localhost',
port: 6379,
retryStrategy: (times) => Math.min(times * 50, 2000)
});
async function getCachedData(key) {
const cached = await redis.get(key);
if (cached) {
return JSON.parse(cached);
}
// Имитация получения данных из БД
const freshData = { id: 1, name: 'Тестовые данные' };
// Сохраняем в кэш на 60 секунд
await redis.setex(key, 60, JSON.stringify(freshData));
return freshData;
}
// Использование
getCachedData('product:42').then(data => console.log(data));
Redis также поддерживает паттерн "кэш-ассид" (cache-aside): приложение сначала проверяет кэш, при промахе загружает данные из БД и сохраняет их в Redis. Это наиболее распространённая стратегия.
Продвинутые возможности Redis
С помощью Redis можно реализовать rate limiting, очереди задач (через списки), сессионные хранилища и даже полнотекстовый поиск (через модуль RediSearch). Для кэширования особенно полезны:
INCR— счётчики посещений.ZADD/ZRANGE— кэширование топ-рейтингов.PUBLISH/SUBSCRIBE— инвалидация кэша в реальном времени.
Memcached: лёгкий и быстрый
Memcached — это распределённая система кэширования, ориентированная исключительно на хранение пар ключ-значение. В отличие от Redis, Memcached не поддерживает постоянное хранение (всё хранится только в RAM) и имеет более простую модель данных. Для Node.js используется пакет memcached.