Row-level и Column-level TTL
TTL (Time-to-Live) — механизм автоматического управления жизненным циклом данных в ClickHouse. Вместо написания cron-задач и ручных DELETE-запросов вы декларативно описываете, когда данные должны исчезнуть или измениться, а ClickHouse применяет эти правила в фоновом режиме во время merge-операций.
Как работает TTL: связь с merge-циклом
TTL-правила применяются исключительно во время merge-операций — когда ClickHouse объединяет несколько part-файлов в один. Это означает, что данные с истёкшим TTL не удаляются мгновенно после INSERT.
TTL применяется только при merge, не сразу после INSERT. Если вставить строки с уже истёкшим TTL, они будут видны в SELECT до следующего merge-цикла или принудительного MATERIALIZE TTL.
Row-level TTL: автоматическое удаление строк
Row-level TTL настраивается на уровне таблицы через предложение TTL. При истечении срока строка полностью удаляется из хранилища.
-- Создание таблицы с row-level TTL
CREATE TABLE events
(
event_time DateTime,
user_id UInt64,
payload String
)
ENGINE = MergeTree
ORDER BY (user_id, event_time)
TTL event_time + INTERVAL 90 DAY;
Выражение event_time + INTERVAL 90 DAY вычисляется для каждой строки. Строки, для которых это выражение уже меньше текущего времени, удаляются при следующем merge.
Допустимые единицы интервала: SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR.
Изменение TTL на существующей таблице
-- Изменить TTL существующей таблицы (новое значение применяется при следующем merge)
ALTER TABLE events
MODIFY TTL event_time + INTERVAL 180 DAY;
-- Полностью удалить TTL с таблицы
ALTER TABLE events
REMOVE TTL;
Column-level TTL: замена значения на дефолт
Column-level TTL задаётся в определении колонки. По истечении срока значение колонки не удаляется — оно заменяется на дефолтное значение типа.
| Тип колонки | Что подставляется после TTL |
|---|---|
UInt8, UInt32, Int64, Float64 | 0 |
String | '' (пустая строка) |
Date, Date32 | 1970-01-01 |
DateTime, DateTime64 | 1970-01-01 00:00:00 |
Array(T) | [] (пустой массив) |
-- Таблица с column-level TTL на sensitive-колонках
CREATE TABLE events_tiered
(
event_time DateTime,
user_id UInt64,
raw_payload String TTL event_time + INTERVAL 7 DAY, -- через 7 дней → ''
ip_address String TTL event_time + INTERVAL 30 DAY, -- через 30 дней → ''
summary String -- хранится бессрочно
)
ENGINE = MergeTree
ORDER BY (user_id, event_time);
Column TTL заменяет значение на дефолт типа, а не на NULL. Даже если тип допускает Nullable, TTL подставит 0 или '', а не NULL. Для GDPR-сценариев, где важно различать “данные удалены” и “данные отсутствуют”, используйте Nullable тип и явную мутацию, либо row-level TTL.
Совмещение row-level и column-level TTL
Таблица может иметь одновременно оба типа TTL:
CREATE TABLE user_activity
(
event_time DateTime,
user_id UInt64,
raw_data String TTL event_time + INTERVAL 7 DAY, -- колонка обнуляется через 7 дней
processed_data String
)
ENGINE = MergeTree
ORDER BY (user_id, event_time)
-- Row TTL: вся строка удаляется через 365 дней
TTL event_time + INTERVAL 365 DAY;
Порядок применения: сначала срабатывает column TTL (значение заменяется), затем — row TTL (строка удаляется). Если row TTL короче column TTL, column TTL никогда не сработает для удалённых строк.
MATERIALIZE TTL: принудительное применение к существующим данным
Команда MATERIALIZE TTL запускает немедленную обработку всех существующих частей таблицы, применяя TTL без ожидания фонового merge.
-- Принудительно применить TTL ко всем существующим данным
ALTER TABLE events MATERIALIZE TTL;
-- Проверить прогресс через system.mutations
SELECT mutation_id, command, is_done, create_time
FROM system.mutations
WHERE table = 'events'
ORDER BY create_time DESC
LIMIT 5;
Альтернативный способ форсировать слияние (менее контролируемый):
-- Принудительный merge конкретной таблицы (не рекомендуется в production на больших таблицах)
OPTIMIZE TABLE events FINAL;
Проверка TTL через system.parts
-- Посмотреть TTL-метаданные на уровне партов
SELECT
name,
rows,
min_ttl_expression,
delete_ttl_expression,
min_time,
max_time
FROM system.parts
WHERE table = 'events'
AND active
ORDER BY min_time;
Ключевые выводы
- TTL применяется только при merge — не сразу после INSERT.
MATERIALIZE TTLилиOPTIMIZE TABLE FINALдля немедленного применения. - Row-level TTL полностью удаляет строку; column-level TTL заменяет значение на дефолт типа (не на NULL).
- Дефолт типа для String — пустая строка
'', для чисел —0, для Date —1970-01-01. ALTER TABLE MODIFY TTLменяет правило для новых merge;MATERIALIZE TTLприменяет его к существующим данным.- Таблица может иметь одновременно row TTL и column TTL на разных колонках с разными интервалами.