Кэширование в JavaScript: Redis, Memcached и встроенные решения от А до Я

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

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

Начать курс

Кэширование в 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 — для продакшен-систем, где нужны сложные структуры, персистентность, репликация и высокая надёжность.

Лучшие практи

Блоги

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