Redis поза межами кешування: сесії, обмеження швидкості, черги та Pub/Sub у Node.js
Сім шаблонів використання Redis для бекендів Node.js — кешування з TTL, ключі OTP, обмеження швидкості, завдання BullMQ, сесії, обмеження Pub/Sub та ситуації, коли не варто використовувати Redis.
Початкові уявлення про Redis полягають у тому, що це «просто кеш»: зберігається значення, встановлюється термін дії, його читають швидше, ніж з бази даних — і все готово.
Цей погляд не є хибним. Він є неповним.
На практиці Redis використовується для швидких відповідей API, спільних сеансів, лічильників зловживань, одноразових кодів, черг задач із відкладеною обробкою, легких розсилок у реальному часі та інших короткочасних значень.
Краще розуміння: розглядати Redis як швидкий внутрішньопам’ятний сховище даних із багатьма функціями на рівні сервера — кешування є лише першою з них.
1. Redis може значно прискорити API
Почніть з кешування.
Розгляньмо GET /products.
Без кешу кожен запит може виглядати так:
Клієнт → API Node.js → PostgreSQL → API Node.js → Клієнт
Дорогий запит при тисячах запитів означає, що база даних повторює одну й ту саму роботу.
Redis може стати проміжним елементом перед цим запитом.
Клієнт → API Node.js → Redis
При успішному пошуку — негайно повертати результат. При невдачі — запитувати PostgreSQL, зберігати результат у Redis, а потім повертати його.
Простий приклад використання ioredis:
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);
async function getProducts() {
const cached = await redis.get("products");
if (cached) {
return JSON.parse(cached);
}
const products = await database.product.findMany();
await redis.set(
"products",
JSON.stringify(products),
"EX",
300
);
return products;
}h
Тут EX означає, що дані у кеші закінчують свою дію через 300 секунд.
Існує ще одна серйозна проблема: анулювання кешу.
Припустимо, у Redis зберігається product:123 → price:500, тоді як у PostgreSQL зараз зберігається значення 600. База даних містить правильну інформацію, але Redis все одно може повернути значення 500.
Кешування — це не просто «зберігати все у Redis». Командам все одно потрібно враховувати:
- TTL
- Анулювання даних у кеші
- Старі дані
- Невдачі під час пошуку
- Помилки кешу
Зберігати значення у Redis легко. Зберігати його правильним — складніше.
2. Redis ідеально підходить для тимчасових даних
Тимчасові значення є ідеальним рішенням: одноразові коди входу, посилання на скидку, токени верифікації, тимчасові блоки сеансу, лічильники зловживань та захисні блокування.
Приклад: видача одноразового коду:
const otp = "482913";
await redis.set(
`otp:${userId}`,
otp,
"EX",
300
);
Одноразовий код автоматично закінчує свою дію через п’ять хвилин.
Не потрібна окрема таблиця для зберігання одноразових кодів, так само як і окрема задача на очищення даних.
Його можна перевірити за допомогою:
const otp = await redis.get(`otp:${userId}`);
Після закінчення терміну дії Redis видаляє ключ згідно з механізмом закінчення терміну.
Саме ця структура робить Redis ідеальним місцем для зберігання тимчасових даних додатків.
3. Redis може допомогти з обмеженням частоти запитів
Візьмемо POST /login.
Без обмежень клієнт може надсилати безліч спроб входу — тисячі запитів поспіль.
Redis може зберігати лічильник для клієнта:
const key = `login-attempts:${ip}`;
const attempts = await redis.incr(key);
if (attempts === 1) {
await redis.expire(key, 60);
}
if (attempts > 10) {
throw new Error("Too many requests");
}
Структура виглядає так: IP → лічильник у Redis → кількість запитів → ліміт.
Це має ще більше значення, коли за лоад-балансером знаходиться кілька серверів API. Лічильники в пам’яті на рівні процесу не є глобальними. Redis забезпечує цим серверам спільний сховище.
4. Redis може використовуватися для виконання фонових завдань
Під час створення облікового запису може знадобитися створити користувача, надіслати лист-привітання, сгенерувати дані, повідомити інший сервіс та виконати іншу роботу.
HTTP-запит не повинен чекати на виконання всіх цих дій.
Краще перенести роботу у чергу:
Клієнт → API → Черга → Відповідь
Потім:
Черга → Робочий процес → Виконання завдання
BullMQ — це поширений варіант для Node.js, який використовує Redis:
await emailQueue.add("welcome-email", {
userId: user.id,
email: user.email,
});
Робочий процес виконує завдання окремо:
const worker = new Worker(
"email",
async (job) => {
if (job.name === "welcome-email") {
await sendWelcomeEmail(job.data.email);
}
},
{
connection: redisConnection,
}
);
API не потрібно чекати на відповідь від постачальника електронної пошти, перш ніж відповісти користувачеві.
Це підходить для завдань, які виконуються повільно, можуть бути перезапущені, залежать від зовнішніх сервісів, є ресурсоємкими з точки зору CPU або не є обов’язковими до моменту відповіді API.
Розділення обов’язків: API обробляє запит; працівник виконує складну роботу.
5. Redis може зберігати сесії
Керування сесіями — ще одна сфера застосування. Ключ на кшталт session:abc123 може містити:
{
"userId": "123",
"role": "ADMIN"
}
Це стає корисним, коли кілька інстанцій бекенду ділять один сховище сесій через Redis.
Важлива відмінність: Redis не робить автоматично аутентифікацію безпечною.
Команди все одно повинні керувати ідентифікаторами сесій, захищеними куки, терміном дії, CSRF у разі потреби, аутентифікацією та авторизацією.
Redis — це інфраструктура, а не стратегія безпеки.
6. Redis може допомогти з функціями в реальному часі
Механізм Pub/Sub дозволяє поширювати події між інстанціями. Один з сценаріїв виглядає так: клієнт звертається до сервера A, надсилає дані в Redis, обробник підписки на сервері B, а потім дані надсилаються іншому клієнту.
Ілюстрація:
await redis.publish(
"notifications",
JSON.stringify({
userId: "123",
message: "Your order has shipped",
})
);
await subscriber.subscribe("notifications")
subscriber.on("message", (channel, message) => {
console.log(channel, message);
});
Корисно для сповіщень, реального оновлення інформації, процесів, пов’язаних з чатом, та поширення подій.
Обмеження: Redis Pub/Sub не є надійною чергою повідомлень.
Якщо важлива надійна обробка, повторні спроби або гарантована доставка, краще використовувати чергу чи Redis Streams залежно від ситуації.
Розуміння цієї різниці має велике значення.
7. Redis стає проблемою, якщо його використовують скрізь
Найважливіший урок: як тільки з’являється можливість використання Redis, виникає спокуса помістити туди все.
Не робіть цього.
Швидкість сама по собі не означає, що кожен даний має знаходитися в пам’яті.
PostgreSQL може залишатися постійним джерелом істини для бізнес-даних, тоді як Redis відповідає за кеш, сесії, OTP, обмеження швидкості та черги.
Практичний підхід полягає у тому, щоб зберігати надійні бізнес-дані в PostgreSQL, а Redis використовувати для операцій з високою частотою, коротким терміном життя даних та для підтримки таких завдань, як черги чи лічильники.
Якщо з самого початку визначити ці межі, можна уникнути багатьох подальших переробок.
Структури даних Redis мають значення
Redis — це не лише ключ → рядок. Він пропонує кілька типів структур.
Рядки
Прості значення, наприклад user:123:name → „Mit“.
Хеші
Кілька полів під одним ключем:
user:123
name → Mit
role → ADMIN
email → example@email.com
Списки
Впорядковані колекції та деякі структури, схожі на черги.
Множини
Унікальні значення.
Впорядковані множини
Елементи, впорядковані за оцінкою — наприклад, табель лідерів:
1000 → Player A
900 → Player B
800 → Player C
Вибір правильної структури часто спрощує проблему.
Поширені помилки в використанні Redis, яких слід уникати
Помилка 1: кешування всього
Не кожен запит потребує кешування. Кешування додає складності. Якщо запит вже достатньо швидкий, Redis може вирішити проблему, якої насправді немає.
Помилка 2: відсутність терміну дії
Тимчасові дані без терміну дії накопичуються. Якщо дані не повинні зберігатися вічно, встановіть для них політику терміну дії.
Помилка 3: використання Redis як постійної бази даних
Якщо Redis містить єдину копію критичних бізнес-даних, система має серйозну залежність. Необхідно знати джерело істинних даних.
Помилка 4: ігнорування збоїв Redis
Вирішіть, що буде робитися, коли Redis не працює. Для багатьох випадків використання кешу прийнятно звернутися до бази даних. Правильна стратегія залежить від ролі, яку виконує Redis.
Помилка 5: використання Redis без розуміння навантаження
Швидкість не є нескінченною. Потрібно все одно зважати на пам’ять, видалення даних, з’єднання, проектування ключів, TTL, серіалізацію, затримки в мережі та вимоги до стабільності зберігання даних.
Як Redis змінює підхід до роботи з бекендом
Багато проектів починаються з обробника запитів, який працює безпосередньо з SQL.
Коли з’являються пам’ятні сховища та відкладені працівники, схема часто ускладнюється: обробник спочатку перевіряє Redis, а потім базу даних; або він записує завдання у чергу для працівника, який взаємодіє з зовнішнім постачальником.
Бекенди стають сукупністю спеціалізованих елементів. Redis — це один з таких елементів, який може виконувати більше ніж одну функцію.
Заключне зауваження
Не впроваджуйте Redis лише тому, що це роблять усі інші.
Використовуйте його тоді, коли ситуація це дозволяє: кешування для часто використовуваних даних, ключі TTL для тимчасових секретів, лічильники для обмеження зловживань, черги для відкладених завдань, спільний сховище сесій між інстансами або система Pub/Sub, коли достатньо легкого розповсюдження даних.
Уникайте ситуації, коли Redis є стандартним рішенням для кожної проблеми бекенду.
Хороші системи — це не ті, у яких найбільший список інструментів. Це ті, де кожен інструмент заслуговує на своє місце.