Кэширование в JavaScript: Redis, Memcached и встроенные решения: от А до Я
Кэширование — это один из ключевых инструментов для повышения производительности веб-приложений. В контексте JavaScript и Node.js грамотное кэширование позволяет снизить нагрузку на базы данных, уменьшить время ответа API и улучшить пользовательский опыт. В этой статье мы разберём три основных подхода: встроенное кэширование в памяти процесса, Memcached и Redis. Вы узнаете, когда какой инструмент выбрать, и получите готовые примеры кода.
Зачем нужно кэширование в Node.js?
Node.js — однопоточная среда с асинхронным вводом-выводом. Каждый запрос к базе данных или внешнему API требует времени. Кэширование позволяет хранить часто запрашиваемые данные в быстром хранилище (оперативная память) и отдавать их без повторных тяжёлых операций. Это особенно важно для:
- Снижения задержек (latency) при повторных запросах
- Уменьшения нагрузки на базы данных (PostgreSQL, MongoDB и др.)
- Экономии вычислительных ресурсов сервера
- Повышения пропускной способности приложения
Встроенное кэширование в Node.js (In-Memory Cache)
Самый простой способ — использовать объект Map или Object в глобальной области видимости. Это подходит для небольших проектов или данных, которые живут в рамках одного процесса.
Пример: Простой кэш с Map
const cache = new Map();
function getCachedData(key) {
if (cache.has(key)) {
const entry = cache.get(key);
if (Date.now() < entry.expiry) {
return entry.data;
} else {
cache.delete(key); // удаляем просроченные данные
}
}
return null;
}
function setCachedData(key, data, ttlMs = 60000) {
cache.set(key, {
data,
expiry: Date.now() + ttlMs
});
}
// Использование:
setCachedData('user:123', { name: 'Alice' }, 30000);
const user = getCachedData('user:123');
console.log(user); // { name: 'Alice' }
Плюсы: простота, нулевая задержка сети, не требует установки дополнительных сервисов.
Минусы: данные не разделяются между несколькими процессами/серверами, ограничены памятью одного процесса.
Memcached: распределённое кэширование
Memcached — это высокопроизводительная распределённая система кэширования в памяти. Она проста, быстра и хорошо подходит для хранения небольших объёмов данных (например, сессий или HTML-фрагментов). Для Node.js существует популярный модуль memcached.
Установка и подключение
npm install memcached
Пример работы с Memcached
const Memcached = require('memcached');
const memcached = new Memcached('localhost:11211');
// Запись данных с TTL (в секундах)
memcached.set('user:456', { name: 'Bob' }, 3600, (err) => {
if (err) console.error('Ошибка записи:', err);
});
// Чтение данных
memcached.get('user:456', (err, data) => {
if (err) {
console.error('Ошибка чтения:', err);
} else if (data) {
console.log('Данные из кэша:', data);
} else {
console.log('Кэш пуст — данные не найдены');
}
});
// Закрытие соединения при завершении работы
process.on('SIGINT', () => {
memcached.end();
});
Плюсы: простота API, высокая скорость, распределение между серверами.
Минусы: нет встроенной репликации, данные теряются при перезапуске, ограниченный набор типов данных.
Redis: мощное кэширование с продвинутыми возможностями
Redis — это не просто кэш, а полноценное хранилище данных в памяти с поддержкой сложных структур (строки, списки, множества, хэши). Он поддерживает персистентность, репликацию и транзакции. Для Node.js используем библиотеку redis.
Установка
npm install redis
Пример: кэширование с Redis (современный async/await)
const { createClient } = require('redis');
async function main() {
const client = createClient();
await client.connect();
// Запись данных с TTL (в секундах)
await client.setEx('user:789', 3600, JSON.stringify({ name: 'Charlie' }));
// Чтение данных
const cached = await client.get('user:789');
if (cached) {
const user = JSON.parse(cached);
console.log('Пользователь из кэша:', user);
} else {
console.log('Кэш пуст — идём в БД');
}
// Закрываем соединение
await client.disconnect();
}
main().catch(console.error);
Продвинутый пример: кэширование с проверкой наличия
const { createClient } = require('redis');
const client = createClient();
async function getCachedOrFetch(key, fetchFunction, ttl = 300) {
const cached = await client.get(key);
if (cached) {
return JSON.parse(cached);
}
const freshData = await fetchFunction();
await client.setEx(key, ttl, JSON.stringify(freshData));
return freshData;
}
// Использование:
const userData = await getCachedOrFetch('user:101', async () => {
// Эмуляция запроса к БД
return { id: 101, name: 'David' };
}, 120);
console.log(userData);
Плюсы: богатый набор структур данных, персистентность, поддержка кластеризации, Pub/Sub, TTL на уровне ключей.
Минусы: требует больше памяти, чем Memcached, более сложен в настройке, но для большинства задач это лучший выбор.
Сравнение: когда что использовать?
- Встроенный кэш (Map) — для односерверных приложений, прототипов и данных, которые не нужно разделять между воркерами.
- Memcached — когда нужна максимальная простота и скорость для простых ключ-значение пар, и не требуется персистентность.
- Redis — для продакшен-систем, где нужны сложные структуры, персистентность, репликация и высокая надёжность.