TTL (Time To Live) — параметър, определящ максималното време, през което данните се считат за актуални. След изтичането на TTL записът се отбелязва като остарял (stale) и трябва да бъде изтрит или обновен. Според Mozilla Developer Network (2026), механизмът TTL е основата на HTTP-кеширането чрез заглавката Cache-Control: max-age и се използва във всички съвременни браузъри и мобилни приложения за оптимизиране на мрежовите заявки.
Основното
TTL (Time To Live) — това е времева марка или интервал, след изтичането на който данните се считат за невалидни. В контекста на кеширането TTL определя колко дълго записът може да се съхранява в кеша, преди да трябва да бъде поискан отново от първоизточника. В мрежовите протоколи TTL ограничава времето на живот на пакета, предотвратявайки безкрайна маршрутизация.
Стойността на TTL винаги се изразява в единици време: милисекунди, секунди, минути или часове. След изтичането на зададеното време записът или се изтрива от кеша, или се отбелязва като stale (остарял). При следващата заявка към остарял запис системата може или да върне stale-данни с последващо обновяване (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 е изтекъл, данните се изтриват или се отбелязват като stale, а заявката се насочва към източника. За оптимизиране на проверката на 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 — това е оптимизация за статични ресурси, предложена в 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също