Ось готовий урок, створений за твоїм майстер-промптом.
🎓 Тема: Expiration strategies та Eviction policies
(Або: Чому твій кеш не гумовий і що з цим робити)
1. 🔥 Вступ: проблема та мотивація
Привіт, друзі! 👋
Уявіть, що ви — власник суперпопулярної кав'ярні. У вас є невеличка вітрина для десертів, куди поміщається рівно 10 чізкейків.
Клієнти йдуть потоком. Ви печете нові торти, ставите їх на вітрину. І тут виникає дві проблеми:
- Проблема свіжості: Чізкейк не може стояти вічно. Якщо йому 3 дні — його треба викинути, інакше клієнт отруїться. Це — Expiration (термін придатності).
- Проблема місця: Ви спекли свіжий «Наполеон», але вітрина забита. Щоб поставити новий торт, треба прибрати якийсь старий. Який саме? Той, що найдавніше стоїть? Чи той, який ніхто не купує? Це — Eviction (витіснення).
В IT все точно так само. Ваша оперативна пам'ять (RAM) — це та сама вітрина. Вона швидка, але дорога і обмежена.
Питання до вас: Що станеться з вашим сервером, якщо ви будете бездумно записувати дані в кеш (наприклад, Redis) і ніколи їх не видалятимете? Правильно! Пам'ять закінчиться, сервер впаде (OOM Kill), і ваш сервіс «ляже» посеред "чорної п'ятниці".
Сьогодні ми розберемося, як керувати пам'яттю так, щоб дані були актуальними, а сервер — живим. Поїхали! 🚀
2. 🧠 Теоретична база (без нудних лекцій)
Давайте розділимо ці два поняття, бо їх часто плутають.
⏳ 1. Expiration (Термін дії)
Це про ЧАС. Ми кажемо даним: "Ти живеш рівно 1 годину. Потім зникаєш". В кешах це називається TTL (Time To Live).
Як це працює під капотом? Думаєте, Redis сидить з секундоміром біля кожного ключа? Ні, це було б занадто дорого для процесора. Зазвичай використовуються дві стратегії (часто разом): * Passive (Lazy): Дані видаляються тільки тоді, коли ви до них звертаєтесь, а час вже вийшов. (Як молоко в холодильнику: ви дізнаєтесь, що воно скисло, тільки коли захочете його налити). * Active: Фоновий процес періодично перевіряє випадкові ключі і видаляє прострочені.
🗑️ 2. Eviction (Витіснення)
Це про ПРОСТІР. Пам'ять заповнена на 100%. Прийшов новий запит на запис. Треба когось "вигнати", щоб звільнити місце.
Тут вмикаються політики витіснення (Eviction Policies). Ось найпопулярніші:
- FIFO (First In, First Out): Хто перший прийшов — перший пішов. Чесно, але не ефективно. Можливо, цей "старий" ключ потрібен усім?
- LRU (Least Recently Used) — ⭐ Золотий стандарт: Видаляємо те, що найдовше не використовували.
- Логіка: Якщо цей мем ніхто не дивився тиждень, навряд чи він знадобиться зараз.
- LFU (Least Frequently Used): Видаляємо те, що використовують найрідше.
- Логіка: Якщо цю картинку завантажили всього 1 раз, а іншу — 1000 разів, то першу видаляємо, навіть якщо її відкрили 5 хвилин тому.
3. 🧪 Приклади (від простого до реального)
Уявімо, що ми працюємо з Redis (або будь-яким key-value сховищем).
Приклад 1: Простий Expiration (TTL)
Ви робите систему скидання пароля. Користувач отримує код на email.
# Псевдокод
# Встановлюємо ключ "reset_token:user_123" зі значенням "5902"
# EX 300 означає "expire 300 seconds" (5 хвилин)
redis.set("reset_token:user_123", "5902", ex=300)
👉 Питання: Що отримає користувач, якщо спробує використати код через 6 хвилин?
👉 Відповідь: NULL (або None). Ключ просто зникне. Це безпека + економія місця.
Приклад 2: Eviction у дії (LRU)
Уявіть, що у вас ліміт пам'яті — всього 3 комірки. Політика — LRU (видаляємо те, що давно не чіпали).
Послідовність дій:
1. Записуємо A. (Кеш: [A])
2. Записуємо B. (Кеш: [A, B])
3. Записуємо C. (Кеш: [A, B, C]) — Кеш повний!
4. Читаємо A. (Оновлюємо "свіжість" A. Тепер найдавніший — B).
5. Треба записати D. Місця немає.
👉 Питання: Кого викине система, щоб записати D?
👉 Відповідь: Вона викине B.
Чому? Бо A ми щойно читали (воно "свіже"), C ми записали недавно, а до B не зверталися найдовше.
Новий стан: [C, A, D] (порядок залежить від реалізації, але суть така).
Приклад 3: Реальна проблема "The Thundering Herd"
Це трохи складніше. У вас є супер-популярна новина. Ви закешували її на 60 секунд. Тисячі людей читають її щосекунди.
О 12:00:00 термін дії кешу спливає (Expiration). О 12:00:01 приходить 5000 запитів. Кешу немає ➡️ Всі 5000 запитів летять прямо в Базу Даних. 🔥 База Даних падає.
Це класична проблема синхронного expiration. Ми поговоримо про рішення у практичній частині.
4. 🛠 Практична частина
Прийшов час розім'яти пальці. Виконуйте подумки або на папері.
🔹 Завдання 1: Класичний TTL
Ви пишете сервіс SMS-авторизації. Користувачу не можна відправляти нову SMS частіше, ніж раз на хвилину. Яку стратегію ви використаєте, щоб заблокувати кнопку "Надіслати ще раз"? (Підказка: Створити ключ з TTL... на скільки?)
🔹 Завдання 2: LRU vs FIFO
У вас є кеш браузера (історія відвіданих сторінок). Ви натискаєте кнопку "Назад". Яка політика витіснення краще підходить для кнопки "Назад", якщо пам'ять обмежена: 1. Видаляти найстарішу відвідану сторінку? 2. Видаляти сторінку, на яку ви заходили найрідше?
🔹 Завдання 3: Міні-кейс
Ви розробляєте Netflix. У вас є кеш для відео-фрагментів. * Є фільм "Титанік" (популярний завжди). * Є "Новини за вчора" (були популярні вчора, сьогодні — ні). * Є серіал, який вийшов сьогодні (пік популярності).
Ваш кеш переповнений. Яку політику Eviction ви оберете: LRU чи LFU? Обґрунтуйте. (Подумайте: якщо ви оберете LFU, чи не застрягнуть "Новини за вчора" в кеші навічно, бо їх вчора подивився мільйон людей?)
🔹 Завдання 4: Питання "А що, якщо..." (Jitter)
Пам'ятаєте приклад із падінням бази даних (Thundering Herd)?
У вас є 1000 товарів, і ви кешуєте ціни на них рівно на 1 годину. Ви запустили скрипт о 10:00.
Що станеться об 11:00? Як це виправити, використовуючи функцію random() при встановленні TTL?
5. 💡 Мислення як у розробника
Як думає Senior Developer, коли налаштовує кеш?
-
"Пам'ять не безкінечна".
- Помилка новачка: Кешувати все підряд без TTL. "Авось знадобиться".
- Думка профі: "Скільки це буде жити? Чи потрібен цей об'єкт через 5 хвилин? Якщо ні — ставимо TTL 300 секунд".
-
"LRU — не панацея, але найкращий старт".
- У 90% випадків LRU (Least Recently Used) — це те, що вам треба. Воно добре адаптується до зміни інтересів користувачів.
- Але якщо у вас є дані, які скануються один раз (наприклад, бекап або індексація) — LRU може "вимити" корисні гарячі дані. Тут треба бути обережним.
-
"Randomness is your friend" (Випадковість — твій друг).
- Щоб уникнути одночасного протухання тисяч ключів (як у завданні 4), профі додають Jitter (тремтіння).
- Замість
TTL = 3600, робітьTTL = 3600 + random(0, 300). Тоді навантаження на базу "розмажеться" у часі.
6. 🧩 Підсумок
Отже, що ми сьогодні поклали собі в "кеш" голови:
- Expiration (TTL): Механізм видалення за часом. Потрібен для свіжості даних.
- Eviction Policies: Механізм звільнення місця, коли пам'ять повна.
- LRU: Найпопулярніша стратегія — видаляємо те, що давно не чіпали.
- Вміємо використовувати TTL для логіки (сесії, ліміти) і розуміємо ризики синхронного вимирання ключів.
Ви тепер вмієте: Не просто "зберігати дані", а керувати їхнім життєвим циклом, рятуючи RAM і нерви адмінів.
🔜 У наступній серії: Ми навчилися зберігати і видаляти дані. Але що робити, якщо дані змінилися в Базі, а в Кеші вони ще старі (і TTL ще не вийшов)? Як повідомити кешу, що він бреше? Готуйтеся, тема наступного уроку — Cache Invalidation (або "Як не показати користувачу стару ціну на iPhone").
Це було CS50... тобто, наш урок! До зустрічі! 💻✨