TTL (Time To Live) — параметр, що визначає максимальний час, протягом якого дані вважаються актуальними. Після закінчення TTL запис позначається як застарілий (stale) і має бути видалений або оновлений. Згідно з Mozilla Developer Network (2026), механізм TTL є основою HTTP-кешування через заголовок Cache-Control: max-age і використовується в усіх сучасних браузерах та мобільних додатках для оптимізації мережевих запитів.
Головне
TTL (Time To Live) — це мітка часу або інтервал, після якого дані вважаються недійсними. У контексті кешування TTL визначає, як довго запис може зберігатися в кеші, перш ніж його потрібно буде перезапитати з першоджерела. У мережевих протоколах TTL обмежує час життя пакета, запобігаючи нескінченній маршрутизації.
Значення TTL завжди виражається в одиницях часу: мілісекунди, секунди, хвилини або години. Після закінчення встановленого часу запис або видаляється з кешу, або позначається як застарілий. При наступному запиті до застарілого запису система може або повернути застарілі дані з наступним оновленням (stale-while-revalidate), або заблокувати запит до отримання свіжих даних.
Вибір TTL — це завжди компроміс між актуальністю даних та продуктивністю. Занадто короткий TTL (1–5 секунд) змушує додаток часто виконувати мережеві запити, зводячи нанівець вигоду від кешування. Занадто довгий TTL (години/дні) підвищує ризик показу користувачеві застарілої інформації. Оптимальне значення залежить від типу даних: курс валют — секунди, погода — хвилини, версія API — години.
TTL — це пасивна інвалідація: дані видаляються автоматично після закінчення часу. Альтернатива — активна інвалідація, коли джерело даних повідомляє кеш про зміни (наприклад, через WebSocket-повідомлення або push-сповіщення). Пасивна інвалідація через TTL простіша в реалізації, але не гарантує миттєвої актуальності. Активна інвалідація складніша, але дозволяє підтримувати дані в актуальному стані без затримок, характерних для TTL.
Механізм TTL можна реалізувати двома способами: абсолютне закінчення (absolute expiration) та відносне закінчення (relative expiration). При абсолютному закінченні запис зберігає конкретний час, коли він стане недійсним. При відносному — фіксується час створення запису та TTL як інтервал, а перевірка виконується обчисленням creationTime + TTL > currentTime.
При кожному запиті до кешу система перевіряє TTL кожного запису. Якщо TTL минув, дані видаляються або позначаються як застарілі, і запит направляється до джерела. Для оптимізації перевірки TTL може використовуватися scheduled-очищення (періодичне видалення всіх прострочених записів) або lazy-очищення (видалення тільки при зверненні до запису). Lazy-очищення ефективніше по пам’яті, оскільки не потребує фонового потоку для сканування всього кешу.
У розподілених системах TTL також використовується для автоматичного вирішення конфліктів. Наприклад, якщо два сервери одночасно записали різні значення для одного ключа, запис із більш пізнім TTL може вважатися пріоритетним. Amazon DynamoDB використовує TTL для автоматичного видалення застарілих записів у таблицях — це вбудована функція, що не потребує ручного керування.
Для підвищення продуктивності при закінченні TTL застосовуються стратегії stale-читання. Stale-while-revalidate — одразу повернути застарілі дані клієнту та одночасно запустити фонове оновлення. Stale-if-error — повернути stale-дані, якщо джерело тимчасово недоступне. Cache-Aside (Lazy Loading) — при промаху кешу завантажити дані з джерела, зберегти в кеші з новим TTL і лише потім повернути клієнту. Кожна стратегія вибирається виходячи з вимог до консистентності даних.
У мобільних додатках TTL — ключовий механізм керування кешем. Розглянемо основні сценарії, де TTL визначає поведінку додатка та користувацький досвід.
HTTP-протокол надає вбудований механізм TTL через заголовки Cache-Control. Директива max-age задає TTL у секундах: Cache-Control: public, max-age=3600 означає, що відповідь можна кешувати на 1 годину. Додаткові директиви s-maxage (для shared-кешів, наприклад, CDN) та stale-while-revalidate задають більш тонке керування. При збігу TTL з expires-заголовком пріоритет має max-age, як більш сучасний стандарт HTTP/1.1.
| Тип даних | Рекомендований TTL | Обґрунтування |
|---|---|---|
| Погода | 10–30 хвилин | Прогнози оновлюються не частіше |
| Курси валют | 15–60 секунд | Висока волатильність |
| Новинна стрічка | 2–5 хвилин | Баланс свіжості та продуктивності |
| Профіль користувача | 5–30 хвилин | Рідко змінюється в сесії |
| Список товарів | 10–60 хвилин | Ціни змінюються не щосекунди |
| Статичні ресурси | 1–24 години | Версіонуються через URL або ETag |
Для зображень TTL може досягати кількох днів, оскільки контент рідко змінюється. Однак мобільні додатки часто використовують гібридний підхід: короткий TTL для прев’ю (30 хвилин — актуальність кадрів) і довгий для повнорозмірних зображень (7 днів). Зображення з HTTP-заголовком Cache-Control: immutable взагалі не повинні перезапитуватися до закінчення TTL — це оптимізація для static-ресурсів, запропонована в RFC 8246. Такі зображення кешуються на рівні ОС (URLCache, OkHttp Cache) без участі додатка.
У мережах TTL використовується не для кешування, а для обмеження часу життя пакетів. Кожен IP-пакет містить поле TTL (8 біт), яке зменшується на 1 кожним маршрутизатором. Коли TTL досягає 0, пакет відкидається, а відправнику повертається ICMP-повідомлення Time Exceeded. Це запобігає нескінченній маршрутизації при петлях у мережі.
DNS-записи мають TTL, який визначає, як довго резолвер (наприклад, ISP DNS-кеш) може зберігати запис без запиту до авторитетного сервера. Типові значення: 300 секунд (5 хвилин) для записів із частими змінами, 86400 секунд (24 години) для стабільних доменів. CDN-сервіси часто встановлюють низький TTL (60–300 секунд) для швидкого перенаправлення трафіку при збоях, тоді як статичні домени можуть мати TTL до 7 днів. При міграції сервера рекомендується спочатку знизити TTL до 60 секунд (за 48 годин до міграції), щоб зміни поширилися швидко.
У мобільних додатках TTL використовується для керування сесіями та токенами доступу. JWT-токени (JSON Web Tokens) містять поле exp (expiration time), яке є абсолютним Unix-часом закінчення. Після закінчення refresh-токен використовується для отримання нового access-токена без повторної аутентифікації. TTL access-токена зазвичай становить 1–24 години, refresh-токена — 7–30 днів. Це баланс між безпекою (короткий TTL знижує ризик витоку) та UX (довгий TTL зменшує частоту повторних логінів).
Вибір TTL — інженерне рішення, що залежить від типу даних, SLA щодо актуальності та вартості повторного запиту. Розглянемо основні стратегії.
Найпростіший підхід — всі записи мають однаковий TTL. Наприклад, кешувати всі API-відповіді на 5 хвилин. Перевага: простота реалізації та передбачувана поведінка. Недолік: не враховує різну частоту змін різних типів даних. Фіксований TTL виправданий для однорідних даних, де всі записи мають однакову «свіжість» — наприклад, курс криптовалют на одній біржі.
TTL динамічно змінюється залежно від поведінки даних. Наприклад, якщо запис рідко оновлюється на сервері, TTL збільшується; якщо оновлюється часто — зменшується. Реалізація може використовувати заголовки HTTP-відповідей: заголовок Age (скільки секунд відповідь уже провела в кеші) та заголовок Date дозволяють розрахувати час, що залишився. Адаптивний TTL дає кращий hit-ratio, але потребує додаткової логіки на клієнті.
Probabilistic Early Expiration (PEE) — техніка, при якій TTL вибирається випадковим чином у заданому діапазоні. Це запобігає «ефекту стада» (thundering herd), коли безліч запитів одночасно закінчуються і всі клієнти одночасно звертаються до джерела. PEE особливо корисна для CDN та кешів з високим навантаженням: замість єдиного TTL у 300 секунд використовується випадкове значення від 240 до 360 секунд, що розподіляє навантаження на джерело рівномірно.
Розглянемо реалізацію кешу з TTL на Kotlin з використанням абсолютного закінчення. Кожен запис зберігає час створення, і при читанні перевіряється, чи не минув TTL.
class TtlCache<K, V>(
private val defaultTtlMs: Long = 300000L
) {
private data class Entry<V>(
val value: V,
val createdAt: Long = System.currentTimeMillis()
)
private val map = ConcurrentHashMap<K, Entry<V>>()
fun get(key: K): V? {
val entry = map[key] ?: return null
if (isExpired(entry)) {
map.remove(key)
return null
}
return entry.value
}
fun put(key: K, value: V, ttlMs: Long = defaultTtlMs) {
map[key] = Entry(value, createdAt = System.currentTimeMillis() + ttlMs)
}
private fun isExpired(entry: Entry<*>): Boolean {
return System.currentTimeMillis() > entry.createdAt
}
fun cleanup() {
map.entries.removeIf { isExpired(it.value) }
}
}
Клас Entry зберігає значення та час створення + TTL (абсолютне закінчення). Метод get перевіряє закінчення при кожному зверненні (lazy-очищення) — прострочені записи видаляються тільки при спробі доступу до них. Метод cleanup можна викликати періодично з фонового потоку для пакетного видалення всіх застарілих записів. ConcurrentHashMap забезпечує потокобезпеку без блокування всього кешу.
В iOS для кешування з TTL зручно використовувати URLCache з налаштуванням memoryCapacity та diskCapacity. Однак URLCache не підтримує індивідуальний TTL для різних запитів. Розглянемо кастомну обгортку NSCache з підтримкою TTL.
final class ApiResponseCache {
private var cache = NSCache<NSString, CacheEntry>()
func getResponse(for url: URL) -> Data? {
guard let entry = cache.object(forKey: url.absoluteString as NSString)
else { return nil }
guard entry.expirationDate > Date() else {
cache.removeObject(forKey: url.absoluteString as NSString)
return nil
}
return entry.data
}
func storeResponse(data: Data, for url: URL, ttl: TimeInterval) {
let entry = CacheEntry(data: data, expirationDate: Date().addingTimeInterval(ttl))
cache.setObject(entry, forKey: url.absoluteString as NSString)
}
}
final class CacheEntry: NSObject {
let data: Data
let expirationDate: Date
}
У цій реалізації NSCache використовується як потокобезпечне сховище. CacheEntry містить Data та expirationDate. При get перевіряється, чи не минув час; якщо минув — запис видаляється та повертається nil. TTL задається в секундах через TimeInterval і може бути різним для кожного URL: типові значення для API-відповідей — 120 секунд для динамічного контенту та 3600 для статичних даних.
Часто задавані питання
Технічно TTL і термін придатності — одне й те саме: інтервал часу, після якого дані вважаються недійсними. Різниця в контексті: термін TTL використовується в IT (кеші, мережі, DNS), а «термін придатності» частіше застосовується в бізнес-логіці (промокоди, підписки). У реалізації обидва механізми ідентичні — порівняння поточного часу з часом закінчення.
Оптимальний TTL вибирається емпірично. Методика: почати з консервативного значення (30–60 секунд), поступово збільшувати до появи скарг на застарілі дані. Моніторити hit-ratio кешу: якщо він нижче 70% — TTL занадто короткий. Враховуйте SLA: для фінансових даних TTL може бути 1 секунда, для новин — 5 хвилин, для профілів — 30 хвилин.
Після закінчення max-age браузер або мобільний додаток вважає відповідь stale (застарілою). При наступному запиті до того ж URL клієнт надсилає запит із заголовком If-None-Match (ETag) або If-Modified-Since. Якщо дані не змінилися, сервер повертає 304 Not Modified без тіла відповіді, і TTL оновлюється. Якщо змінилися — сервер повертає 200 з новими даними та новим Cache-Control.
Технічно TTL може бути дуже великим (max-age=31536000 — 1 рік), але це рідко виправдано. Навіть статичні ресурси можуть змінитися, і клієнт не дізнається про це до закінчення TTL. Рекомендується використовувати versioned URLs (style.css?v=2) з довгим TTL: при зміні файлу URL змінюється, а старий кеш автоматично застаріває.
TTL та стратегії витіснення (LRU, FIFO) вирішують різні завдання. TTL визначає, коли дані стають неактуальними — це часовий критерій. LRU та FIFO визначають, які дані видаляти при переповненні кешу — це просторовий критерій. Вони можуть комбінуватися: запис видаляється, якщо минув TTL АБО кеш переповнений (по LRU/FIFO). У production-системах обидва механізми працюють спільно.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.