Пошук уроків, статей та іншого контенту
Налаштуєте автоматичне завершення дії кешованих даних та підберете TTL відповідно до вимог актуальності.
TTL (Time To Live, «час життя») — це період, протягом якого запис вважається дійсним після створення або оновлення.
Коли TTL завершується, запис стає простроченим і більше не повинен використовуватися. Залежно від сховища запис:
видаляється одразу;
видаляється під час наступного звернення;
залишається фізично, але вважається недійсним;
очищується фоновим процесом.
TTL часто використовують для кешу:
запит → перевірити кеш
├─ запис актуальний → повернути його
└─ запис прострочений → отримати свіжі дані та оновити кешНаприклад, курс валюти можна кешувати на 5 хвилин. Після завершення TTL застосунок має отримати нове значення.
Кеш прискорює доступ до даних і зменшує кількість запитів до основного сховища або зовнішнього сервісу. Проте кешована копія поступово застаріває.
TTL допомагає знайти баланс між:
актуальністю даних;
швидкістю відповіді;
навантаженням на основне сховище;
обсягом кешу.
Без TTL кеш може містити старі записи необмежено довго. Це призводить до таких проблем:
користувач бачить застарілу інформацію;
у кеші накопичуються непотрібні дані;
після зміни запису кеш продовжує повертати стару версію;
використовується більше пам’яті або дискового простору.
Під час запису даних система зберігає не лише значення, а й момент завершення його дії.
Наприклад:
ключ: product:42
значення: { "name": "Keyboard", "price": 100 }
TTL: 300 секунд
час завершення: 12:05:00Якщо запис створили о 12:00:00, він дійсний до 12:05:00. Після цього його не можна повертати як актуальний.
У спрощеному вигляді перевірка виглядає так:
якщо поточний час < час завершення:
повернути значення
інакше:
вважати запис простроченимВажливо розрізняти:
TTL — скільки часу запис може жити;
час завершення — конкретний момент, коли він стане простроченим.
Нижче наведено просту реалізацію кешу в пам’яті процесу. Кожен запис має значення і час завершення.
class TTLCache {
constructor() {
this.entries = new Map();
}
set(key, value, ttlMs) {
if (ttlMs <= 0) {
throw new Error("TTL має бути більшим за нуль");
}
const expiresAt = Date.now() + ttlMs;
this.entries.set(key, {
value,
expiresAt
});
}
get(key) {
const entry = this.entries.get(key);
if (!entry) {
return undefined;
}
if (Date.now() >= entry.expiresAt) {
this.entries.delete(key);
return undefined;
}
return entry.value;
}
delete(key) {
return this.entries.delete(key);
}
}
const cache = new TTLCache();
cache.set("user:42", { name: "Олена" }, 1000);
console.log(cache.get("user:42"));
// { name: "Олена" }
setTimeout(() => {
console.log(cache.get("user:42"));
// undefined: запис прострочений і видалений під час читання
}, 1100);У цьому прикладі використано ліниве видалення: прострочений запис видаляється не точно в момент завершення TTL, а під час наступного виклику get.
Це нормальна поведінка для багатьох кешів. Проте якщо прострочені записи займають багато пам’яті, потрібне також періодичне фонове очищення.
Система перевіряє TTL лише під час читання запису.
Переваги:
проста реалізація;
не потрібен окремий фоновий процес;
немає зайвих перевірок записів, до яких ніхто не звертається.
Недолік:
запис може залишатися в пам’яті після завершення TTL, якщо його більше ніхто не читає.
Окремий процес або таймер періодично перевіряє записи та видаляє прострочені.
Переваги:
пам’ять швидше звільняється;
простіше контролювати кількість записів у кеші.
Недоліки:
потрібні додаткові перевірки;
очищення не обов’язково відбувається точно в момент завершення TTL;
у великому кеші повний перегляд усіх записів може бути дорогим.
На практиці часто поєднують обидві стратегії:
під час читання не повертати прострочені дані;
фоновим процесом видаляти записи, які більше не потрібні.
Такий підхід не залежить від того, чи буде прострочений запис прочитано знову.
TTL потрібно визначати з вимог до актуальності конкретних даних. Універсального значення для всіх записів немає.
Потрібно відповісти на запитання:
Як довго старе значення може бути прийнятним?
Що станеться, якщо користувач побачить старі дані?
Як часто змінюються дані?
Яке навантаження створює отримання свіжих даних?
Чи можна тимчасово не отримати нове значення?
Орієнтовні приклади:
результат пошуку товарів — від кількох секунд до кількох хвилин;
список категорій — від кількох хвилин до години;
курс валюти — залежить від вимог, часто від секунд до хвилин;
конфігурація, що змінюється рідко, — від хвилин до годин;
одноразовий код підтвердження — короткий TTL, наприклад кілька хвилин.
Ці значення не є готовими правилами. Вони лише показують, що TTL має відповідати призначенню даних.
Якщо дані змінюються часто, довгий TTL збільшує ймовірність повернення застарілого значення.
Наприклад, якщо залишок товару може змінюватися щосекунди, TTL у 24 години неприйнятний. Користувачі можуть бачити товар як доступний, хоча його вже немає.
Якщо ж дані змінюються раз на місяць, TTL у кілька секунд може створити зайве навантаження на базу даних без помітної користі.
Практичне правило:
Обирайте TTL не довшим за допустимий період застарілості даних.
Якщо бізнес допускає застарілість не більше 5 хвилин, TTL не повинен перевищувати 5 хвилин без додаткового механізму оновлення.
TTL гарантує, що запис стане недійсним через певний час. Але іноді потрібно прибрати старе значення одразу після зміни даних.
Наприклад:
товар був у кеші;
адміністратор змінив його ціну;
TTL завершиться лише через годину;
користувачі протягом години бачитимуть стару ціну.
У такому випадку після зміни основного запису кеш потрібно інвалідувати:
оновити товар у базі даних
видалити product:42 з кешуTTL і інвалідація вирішують різні задачі:
TTL — автоматично обмежує максимальний час життя кешу;
інвалідація — достроково видаляє дані, коли відома їхня зміна.
Надійна система часто використовує обидва механізми.
Є два поширені підходи до продовження терміну життя.
TTL задається під час запису й не змінюється під час читання.
запис створено о 10:00
TTL: 10 хвилин
запис стає простроченим о 10:10Навіть якщо запис часто читають, він завершиться о 10:10.
Такий підхід добре підходить для даних, які потрібно регулярно оновлювати.
Термін життя продовжується після кожного звернення.
запис не читали 10 хвилин → він завершується
прочитали запис → його TTL знову становить 10 хвилинЦе може бути корисним для тимчасових сесій або даних, які мають жити, поки ними користуються.
Потрібно обережно використовувати цей підхід для звичайного кешу: популярний запис може ніколи не завершити дію й довго залишатися застарілим.
Один і той самий TTL для всього кешу зазвичай є поганим рішенням. Дані мають різні вимоги.
Наприклад:
product:42 TTL 5 хвилин
categories TTL 1 година
exchange-rates TTL 1 хвилина
search:query TTL 30 секундДля кожного типу запису варто окремо визначити:
допустиму застарілість;
частоту оновлення;
вартість отримання свіжих даних;
наслідки використання старого значення.
Коли запис прострочений, застосунок має визначити, що робити далі.
Найпростіший варіант:
кеш-промах → запит до основного джерела → записати результат у кеш → повернути результатЯкщо основне джерело тимчасово недоступне, можливі різні рішення:
повернути помилку;
повернути застаріле значення, якщо це дозволено;
повторити запит;
повернути резервне значення.
Для даних із суворими вимогами до актуальності не слід повертати прострочений кеш без чітко визначеного правила.
Коли TTL короткий, записи частіше завершують дію. Це збільшує кількість кеш-промахів і запитів до основного джерела.
Коли TTL довгий:
кеш працює ефективніше;
але дані можуть бути застарілими довше.
Під час вибору TTL важливо спостерігати за показниками:
частка кеш-попадань;
частка кеш-промахів;
середній час відповіді;
кількість запитів до основного сховища;
вік даних, які повертаються користувачам;
кількість прострочених записів.
TTL варто змінювати на основі вимірювань, а не лише припущень.
Довгий TTL зменшує навантаження, але може призвести до тривалого використання старих даних.
Короткий TTL робить дані актуальнішими, але кеш майже не допомагає: записи постійно завершують дію, а основне джерело отримує більше запитів.
Записи без TTL можуть накопичуватися в кеші та займати пам’ять необмежено довго.
Якщо дані повинні змінитися в кеші одразу після оновлення, очікування завершення TTL не підходить.
Іноді застаріле значення краще за помилку, але це має бути свідомим рішенням. Для цін, залишків товару або прав доступу така поведінка може бути небезпечною.
В одному API TTL може задаватися в секундах, а в іншому — у мілісекундах. Плутанина між ними здатна створити як майже миттєве завершення записів, так і їхнє життя протягом надто довгого часу.
Якщо дані оновлюються із затримкою, TTL потрібно обирати з урахуванням цієї затримки. Формальне завершення TTL не гарантує, що нове значення вже доступне в основному джерелі.
TTL — це максимальний час життя запису.
Після завершення TTL запис не можна вважати актуальним.
TTL допомагає контролювати застарілість даних і розмір кешу.
Короткий TTL покращує актуальність, але збільшує навантаження.
Довгий TTL зменшує кількість запитів, але підвищує ризик застарілих даних.
TTL потрібно підбирати окремо для різних типів записів.
TTL не замінює інвалідацію після відомої зміни даних.
Прострочені записи можна видаляти ліниво, у фоновому режимі або комбінованим підходом.
Правильність вибору TTL потрібно перевіряти за вимірюваннями та вимогами до актуальності.