Привет, Хабр!
Сегодня мы коротко рассмотрим, как работает TTL в трёх популярных NoSQL‑хранилищах — Redis, MongoDB и Cassandra.
Что такое TTL и зачем он вообще нужен
TTL (Time To Live) — это не просто параметр в одну строчку, это фундаментальный способ сказать данным: «живи до… и исчезни без лишнего шума». По сути, это встроенная система самоудаления, срабатывающая без триггеров, без ручного вмешательства, без фоновых джобов. Задал срок — и система сама решает, когда пора отправить объект на покой.
Когда нужен TTL?
Кеш с известным сроком жизни
У тебя есть тяжёлый запрос в базу, но данные из него актуальны максимум 5 минут. Ты кладёшь результат в Redis или Memcached, ставишь TTL в 300 секунд — и больше не паришься. Данные сами удалятся, кеш сам обновится. Без TTL пришлось бы писать костыли: «а давай поставим updatedAt
, проверим, сколько прошло, и если много — удалим вручную». TTL решает всё это встроенно.
Временные сессии, токены, капчи
Авторизация, верификация, восстановление паролей — все эти процессы строятся на одноразовых или временных данных. Сессионный токен должен жить, скажем, 30 минут. Капча — 90 секунд. TTL позволяет задать эту логику прямо в момент записи.
Soft delete без флагов
Иногда не хочется использовать is_deleted = true
. Особенно когда данные реально должны исчезать. Например, временная корзина, истекающие права доступа, устаревшие подписки. TTL — это альтернатива флагам и ручной уборке: данные исчезают без участия разработчика. Флаги — штука хрупкая: забудешь фильтр, и вдруг старый мусор всплывает в API. TTL — это отсутствие самого объекта, и значит — никакой ошибки в выборке.
Рассмотрим конкретные реализации.
Redis
В Redis TTL задаётся на уровне ключей. И это чувствуется в API — всё заточено под скорость и прямоту.
Когда создаёшь ключ — TTL можно задать прямо при установке:
# Установить значение и задать TTL сразу
SET session:123 "user_id_456" EX 60
SET verification:code_abc "9510" PX 300000 # 5 минут в миллисекундах
Если ключ уже существует, TTL можно установить отдельно:
# Установить TTL на существующий ключ
EXPIRE session:123 120 # В секундах
PEXPIRE session:123 1500 # В миллисекундах
Если нужно, чтобы ключ умер в конкретный момент времени — есть EXPIREAT
:
# Установить время "смерти" как UNIX timestamp
EXPIREAT session:123 1724010000
PEXPIREAT session:123 1724010123456 # Миллисекунды
TTL можно и снять:
# Сделать ключ бессмертным
PERSIST session:123
И конечно, TTL можно проверить:
TTL session:123 # вернёт оставшееся время (секунды)
PTTL session:123 # то же самое, но в миллисекундах
-1
означает, что TTL не установлен, -2
— ключа больше нет.
Lazy vs Active Expiration
Пример с lazy:
# ключ просрочен, но пока не удалён
GET session:expired_key # Redis удалит его в этот момент
Active expiration — это то, что Redis делает в фоне, но если много ключей истекает одновременно, может возникнуть лаг.
Чтобы настроить Redis на агрессивную чистку:
# redis.conf
hz 100
active-expire-effort 10
Но по опыту в проде TTL лучше делать немного разным, чтобы избежать массового одновременного удаления:
import random
ttl = 60 + random.randint(0, 15)
redis.set("token:user_123", access_token, ex=ttl)
MongoDB
В MongoDB TTL работает через индексы. Это не таймер на документ — это правило, встроенное в индекс. Пример установки:
// Документ будет жить 1 час после createdAt
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 3600 }
)
createdAt
должен быть Date
. Если это String
или Number
— TTL просто игнорируется. Документ живёт вечно.
Добавим документ:
db.sessions.insertOne({
_id: "abc123",
userId: "u789",
createdAt: new Date()
})
Mongo начнёт отсчёт TTL от поля createdAt
, а не от момента вставки. Чтобы обновить TTL — нужно обновить это поле:
// Обновим TTL вручную, обновив дату
db.sessions.updateOne(
{ _id: "abc123" },
{ $set: { createdAt: new Date() } }
)
Пример ситуации, где TTL не обновится:
// TTL НЕ обновится, так как createdAt не трогали
db.sessions.updateOne(
{ _id: "abc123" },
{ $set: { status: "active" } }
)
Удалить TTL‑индекс можно так:
db.sessions.dropIndex("createdAt_1")
После этого Mongo перестанет удалять старые записи — даже если поле createdAt
продолжит обновляться.
Если нужна точность — лучше использовать другое поле, например expiresAt
, и удалять вручную:
db.sessions.insertOne({
_id: "abc123",
userId: "u789",
expiresAt: new Date(Date.now() + 10 * 60 * 1000)
})
// потом кроном или фоном:
db.sessions.deleteMany({
expiresAt: { $lt: new Date() }
})
Cassandra
Cassandra работает на низком уровне. TTL задаётся при вставке или обновлении, с ключевым словом USING TTL
.
Пример при вставке:
-- строка живёт 1 час
INSERT INTO sessions (id, user_id)
VALUES ('abc123', 'user456')
USING TTL 3600;
Пример при обновлении:
-- обновим только поле email, TTL применяется только к нему
UPDATE sessions USING TTL 1800
SET email = 'a@x.com'
WHERE id = 'abc123';
Если хочется, чтобы TTL продлился для всей строки — можно обновить все поля:
-- безопасное продление TTL всей строки
UPDATE sessions USING TTL 7200
SET user_id = 'user456',
email = 'a@x.com',
last_seen = toTimestamp(now())
WHERE id = 'abc123';
Проверить TTL можно так:
-- покажет оставшийся TTL (в секундах) для поля
SELECT TTL(user_id), TTL(email)
FROM sessions
WHERE id = 'abc123';
Если значение не имеет TTL — вернётся null
.
Когда TTL истекает, Cassandra создаёт tombstone. Они удаляются позже во время compaction, но пока лежат и замедляют запросы.
Пример следствия:
-- Запрос, который начинает тормозить из-за миллиона tombstones
SELECT * FROM user_logs WHERE user_id = 'u123';
Если ты не следишь за этим — таблица превращается в болото.
Следи за состоянием:
nodetool cfstats mykeyspace.sessions
И смотри на поля вроде Tombstone count
, Live cells
, Droppable tombstones
.
Если хочется принудительно удалить всё, что протухло, можно запускать nodetool compact
, но осторожно: это затратная операция.
Заключение
TTL — это не просто галочка «пусть сам удалится», а реальный инструмент управления данными. В Redis — быстро и точно, но можно перегреть систему. В Mongo — удобно, но не жди точности. В Cassandra — мощно, но шаг влево, шаг вправо — куча tombstone»ов и тормоза.
TTL надо понимать и чувствовать. Если ставишь — знай, когда, где и зачем. Пиши в комментах, как ты используешь TTL, где облажался или, наоборот, спас систему.
Если ваши проекты требуют работы с огромными объемами данных и сложной архитектурой, важно знать, как эффективно использовать инструменты и технологии для оптимизации процессов. На этих уроках вы получите практические знания, которые помогут вам решать актуальные задачи с помощью передовых решений.
10 июля, 20:00 — Архитектура и дизайн систем на основе NoSQL в облаках
Основы NoSQL в облаках, настройка баз и масштабирование в популярных платформах.24 июля, 20:00 — Практические кейсы использования ClickHouse
Обработка больших данных с помощью ClickHouse, примеры из веб-логов, IoT и финансов.
А чтобы проверить свой уровень знаний для обучения на курсе "NoSQL", пройдите бесплатное вступительное тестирование.