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 — это оптимизация для 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 дней. Это баланс между безопасностью (короткий TLL снижает риск утечки) и UX (длинный TLL уменьшает частоту повторных логинов).
Выбор 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также