Модуль 12

Expiration strategies та eviction policies

Ось готовий урок, створений за твоїм майстер-промптом.


🎓 Тема: Expiration strategies та Eviction policies

(Або: Чому твій кеш не гумовий і що з цим робити)


1. 🔥 Вступ: проблема та мотивація

Привіт, друзі! 👋

Уявіть, що ви — власник суперпопулярної кав'ярні. У вас є невеличка вітрина для десертів, куди поміщається рівно 10 чізкейків.

Клієнти йдуть потоком. Ви печете нові торти, ставите їх на вітрину. І тут виникає дві проблеми:

  1. Проблема свіжості: Чізкейк не може стояти вічно. Якщо йому 3 дні — його треба викинути, інакше клієнт отруїться. Це — Expiration (термін придатності).
  2. Проблема місця: Ви спекли свіжий «Наполеон», але вітрина забита. Щоб поставити новий торт, треба прибрати якийсь старий. Який саме? Той, що найдавніше стоїть? Чи той, який ніхто не купує? Це — 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, коли налаштовує кеш?

  1. "Пам'ять не безкінечна".

    • Помилка новачка: Кешувати все підряд без TTL. "Авось знадобиться".
    • Думка профі: "Скільки це буде жити? Чи потрібен цей об'єкт через 5 хвилин? Якщо ні — ставимо TTL 300 секунд".
  2. "LRU — не панацея, але найкращий старт".

    • У 90% випадків LRU (Least Recently Used) — це те, що вам треба. Воно добре адаптується до зміни інтересів користувачів.
    • Але якщо у вас є дані, які скануються один раз (наприклад, бекап або індексація) — LRU може "вимити" корисні гарячі дані. Тут треба бути обережним.
  3. "Randomness is your friend" (Випадковість — твій друг).

    • Щоб уникнути одночасного протухання тисяч ключів (як у завданні 4), профі додають Jitter (тремтіння).
    • Замість TTL = 3600, робіть TTL = 3600 + random(0, 300). Тоді навантаження на базу "розмажеться" у часі.

6. 🧩 Підсумок

Отже, що ми сьогодні поклали собі в "кеш" голови:

  1. Expiration (TTL): Механізм видалення за часом. Потрібен для свіжості даних.
  2. Eviction Policies: Механізм звільнення місця, коли пам'ять повна.
  3. LRU: Найпопулярніша стратегія — видаляємо те, що давно не чіпали.
  4. Вміємо використовувати TTL для логіки (сесії, ліміти) і розуміємо ризики синхронного вимирання ключів.

Ви тепер вмієте: Не просто "зберігати дані", а керувати їхнім життєвим циклом, рятуючи RAM і нерви адмінів.


🔜 У наступній серії: Ми навчилися зберігати і видаляти дані. Але що робити, якщо дані змінилися в Базі, а в Кеші вони ще старі (і TTL ще не вийшов)? Як повідомити кешу, що він бреше? Готуйтеся, тема наступного уроку — Cache Invalidation (або "Як не показати користувачу стару ціну на iPhone").

Це було CS50... тобто, наш урок! До зустрічі! 💻✨