Коротка відповідь. Кеш потрібен тоді, коли ті самі дані читаються значно частіше, ніж змінюються, а джерело даних дороге: повільний запит до бази, зовнішній API, важке обчислення. Кеш шкодить, коли дані мають бути завжди актуальними або коли його додають «про запас» без виміряної проблеми. Redis – найпоширеніший інструмент кешування: сховище структур даних у пам'яті з часом відповіді, що вимірюється частками мілісекунди.
Що таке Redis і чим він відрізняється від бази
Redis тримає дані в оперативній пам'яті й працює зі структурами: рядки, хеші, списки, множини, sorted sets. Звідси і сила, і межі:
- Швидкість. Читання з пам'яті на порядки швидше за дисковий запит із JOIN-ами.
- Простота моделі. Ключ → значення. Немає JOIN, немає ad-hoc запитів «знайди всі замовлення з товаром X».
- Пам'ять скінченна. Redis зазвичай зберігає гарячу підмножину даних, а не все.
- Довговічність – опційна. Персистентність налаштовується, але типова роль Redis – дані, які МОЖНА втратити і перебудувати з основної бази.
Тому правильна ментальна модель: Redis доповнює базу, а не замінює її. Першоджерело для деталей: redis.io/docs.
Типові кейси
1. Кеш дорогих запитів (cache-aside)
Найпоширеніший патерн: спершу дивимось у кеш, на промах ідемо в базу і кладемо результат у кеш із TTL:
const CACHE_TTL_SECONDS = 300;
async function getProductCard(productId: string): Promise<ProductCard> {
const cacheKey = `product:card:${productId}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached) as ProductCard;
}
// Промах: читаємо з бази (дорогий запит із JOIN-ами)
const card = await db.loadProductCard(productId);
await redis.set(cacheKey, JSON.stringify(card), 'EX', CACHE_TTL_SECONDS);
return card;
}
async function updateProduct(productId: string, patch: ProductPatch) {
await db.updateProduct(productId, patch);
// Інвалідація: видаляємо ключ, наступне читання перебудує кеш
await redis.del(`product:card:${productId}`);
}Дві речі, про які питають на співбесіді: що станеться, якщо забути del при оновленні (користувачі бачитимуть старі дані до закінчення TTL), і чому TTL потрібен навіть із інвалідацією (страховка від пропущених оновлень і обмеження пам'яті).
2. Сесії та тимчасові дані
Сесії, коди підтвердження, стани візардів – дані з природним терміном життя. TTL робить прибирання автоматичним:
await redis.set(`session:${sessionId}`, JSON.stringify(sessionData), 'EX', 60 * 60 * 24);3. Rate limiting
Лічильник запитів на користувача у вікні часу:
async function isRateLimited(userId: string): Promise<boolean> {
const key = `rate:${userId}:${Math.floor(Date.now() / 60000)}`; // хвилинне вікно
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, 90);
}
return count > 60; // максимум 60 запитів на хвилину
}INCR атомарний, тому лічильник коректний і при паралельних запитах – це важлива деталь для відповіді.
4. Черги і pub/sub – коротко
Списки й streams у Redis використовують як легкі черги задач (відкладене надсилання листів, обробка зображень). Для співбесіди достатньо знати, що це можливо, і чесно сказати, коли варто брати спеціалізований брокер: гарантії доставлення, ретраї, dead letter queues.
Стратегії інвалідації
- TTL (time to live). Найпростіша і найнадійніша: дані застарівають самі. Підходить, коли невелика затримка актуальності прийнятна.
- Інвалідація при записі (del у транзакції оновлення). Точніша, але вимагає дисципліни: КОЖЕН шлях оновлення даних має чистити кеш.
- Write-through. Запис іде і в базу, і в кеш одразу. Кеш завжди теплий, але запис повільніший і код складніший. На практиці найчастіше комбінують cache-aside + TTL.
Класична цитата індустрії: інвалідація кешу – одна з двох по-справжньому складних задач у програмуванні. На співбесіді це не жарт, а натяк: покажи, що бачиш складність.
Redis чи кеш у пам'яті процесу?
Найдешевший кеш – звичайна Map у пам'яті самого сервера: нуль інфраструктури, нуль мережі. Для одного інстанса і невеликих довідникових даних цього часто досатньо. Redis стає потрібним, коли:
- інстансів кілька, і кеш має бути СПІЛЬНИМ (інакше кожен сервер тримає свою версію даних і інвалідація перетворюється на лотерею);
- дані мають пережити рестарт процесу (деплой скидає in-memory кеш повністю – і кожен деплой б'є по базі холодним стартом);
- потрібні атомарні операції між інстансами: лічильники, rate limiting, блокування.
Це теж хороша відповідь на співбесіді: показати, що ти обираєш найпростіший інструмент, який вирішує задачу, а не одразу тягнеш інфраструктуру.
Коли кеш шкодить
- Дані мають бути точними зараз. Залишки товару на оплаті, баланс рахунку. Кешувати можна відображення, але рішення ухвалюється по базі.
- Проблеми ще немає. Кеш до виміряного вузького місця – це додаткова інфраструктура, нові класи багів (розсинхрон) і складніше налагодження. Спочатку профілювання й індекси, потім кеш.
- Дані змінюються частіше, ніж читаються. Кеш промахується постійно і лише додає латентність.
- Кеш став єдиним джерелом правди. Якщо втрата Redis кладе продукт, це вже не кеш, а база без гарантій бази.
Типові помилки при роботі з кешем
- Кешування на рівні «всього». Кеш без вимірювання: незрозуміло, що він прискорив, зате зрозуміло, що дані інколи старі. Спершу знайди повільне місце профілюванням, потім кешуй саме його.
- Один TTL на все. Картка товару може жити 5 хвилин, курс валют – 30 секунд, довідник країн – добу. TTL – це продуктове рішення про допустиму застарілість, а не константа з туторіалу.
- Ключі без схеми іменування.
product:card:{id}можна знайти і масово інвалідувати; ключcache_1234через місяць не розшифрує ніхто. Домовся про префікси одразу. - Великі об'єкти в кеші. Класти в Redis мегабайтні JSON-и означає платити пам'яттю і мережею за дані, з яких потрібні три поля. Кешуй те, що реально споживається.
- Логіка на кеші як на базі. Перевірка «чи існує користувач» по кешу, який може протухнути, – джерело важковідтворюваних багів. Рішення ухвалюються по джерелу правди.
Запитання зі співбесід
Що станеться, коли Redis впаде? Правильна відповідь залежить від ролі: якщо це кеш, застосунок працює повільніше, навантаження йде в базу (і варто мати захист від cache stampede); якщо там сесії, користувачі розлогіняться. Висновок, який хочуть почути: падіння кешу не має бути падінням продукту.
Що таке cache stampede і як захиститися? Популярний ключ протух, тисяча запитів одночасно пішла в базу перебудовувати той самий кеш. Захист: блокування на перебудову (лише один запит будує, решта чекає або віддає stale), рознесені TTL.
Чому не тримати всі дані в Redis, якщо він такий швидкий? Пам'ять дорога і скінченна, модель ключ-значення не дає складних запитів, довговічність слабша за дискову базу з WAL. Швидкість – не єдина вимога до сховища.
Що потренувати
- Реалізуй cache-aside з прикладу вище над будь-яким повільним запитом свого pet-проєкту і виміряй різницю.
- Зламай його: онови дані без інвалідації і подивись на наслідки. Відчуття «звідки беруться stale-дані» коштує більше за конспект.
- Сформулюй одним абзацом, що саме ти кешуєш у своєму проєкті й чому саме з таким TTL.
Кеш – типова тема другої половини backend-співбесіди; перша зазвичай про Node.js і HTTP-сервіси та вибір бази даних. А якщо ціль – системна підготовка, є план на 30 днів.