Zpět na domů

TTL v ClickHouse: správa životního cyklu dat

Článek popisuje vestavěný mechanismus TTL v ClickHouse pro automatické řízení životního cyklu dat: mazání starých řádků, přesun na HDD nebo do S3 (tiered storage), agregaci podrobných dat do souhrnů pomocí GROUP BY, anonymizaci osobních sloupců pro GDPR. Probírá se nastavení procesu na pozadí, MATERIALIZE TTL a kombinace s partitionováním.

TTL v ClickHouse: kompletní průvodce správou dat
Advertisement 728x90

TTL v ClickHouse: Automatické řízení životního cyklu dat

1. Proč je TTL potřeba – problém „zapomněl jsem smazat“

Vraťme se k našemu online casinu. Ukládáš všechny sázky hráčů. Za měsíc má tabulka 500 GB. Za rok – 5 TB. Disky se plní, dotazy zpomalují a stará data jsou potřeba čím dál méně. Majitel firmy říká: „Hráči se dívají jen na posledních 30 dní sázek a pro reporty potřebujeme jen agregovaný součet za stará období.“

Můžeš napsat skript, který jednou denně smaže staré partitiony (jak jsme se učili v článku o partitionování). To ale vyžaduje externí plánovač (cron), samostatný skript, monitorování jeho běhu a zpracování chyb.

TTL (Time-To-Live – „doba života“) řeší tento problém na úrovni databáze. Je to vestavěný mechanismus ClickHouse, který automaticky:

Google AdInline article slot
  • maže staré řádky,
  • přesouvá je na levnější disky (HDD, S3),
  • agreguje stará data (sbalí detaily do souhrnů),
  • anonymizuje osobní údaje (GDPR).

To vše probíhá na pozadí, bez tvého zásahu, podle plánu, který nastavíš v SQL.

Analogický příklad z reálného života: TTL je jako pronájem skladovacího prostoru. Domluvíš se s majitelem: „Zboží, které leží déle než 30 dní, přesuň do vzdáleného levného oddílu. Pokud leží déle než rok, vyhoď.“ Majitel sám hlídá termíny a dělá práci, nemusíš mu to neustále připomínat.

2. TTL na úrovni řádků – mazání starých záznamů

Nejjednodušší varianta: řádky žijí určitou dobu, pak jsou smazány.

Google AdInline article slot

CREATE TABLE s TTL

-- Vytvoříme tabulku sázek, kde řádky žijí 90 dní
CREATE TABLE bets
(
    user_id     UInt64,
    amount      Decimal(18,2),
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 90 DAY;   -- Po 90 dnech od created_at je řádek smazán

Co se zde děje:

  • TTL created_at + INTERVAL 90 DAY – pro každý řádek se vypočítá datum vypršení: created_at + 90 dní. Jakmile aktuální datum (today()) překročí toto datum, řádek je označen k smazání.
  • Proces na pozadí (obvykle jednou denně) prochází granule a maže řádky s vypršeným TTL.
  • Mazání probíhá na úrovni partů (parts) – ClickHouse přepíše part bez smazaných řádků.

ALTER TABLE – přidání nebo změna TTL

Hlavní výhoda: TTL lze přidat k existující tabulce bez jejího přetvoření.

-- Přidáme TTL k existující tabulce
ALTER TABLE bets MODIFY TTL created_at + INTERVAL 90 DAY;

-- Změníme dobu života z 90 na 180 dní
ALTER TABLE bets MODIFY TTL created_at + INTERVAL 180 DAY;

-- Odebereme TTL (data budou uložena navždy)
ALTER TABLE bets REMOVE TTL;

Proč je to důležité? Protože v reálném životě se požadavky na ukládání mění. Nejdřív sis myslel, že je potřeba ukládat všechno navždy. Pak se ukázalo, že zálohy zabírají místo a analytici stará data nepotřebují. S MODIFY TTL změníš pravidlo jedním řádkem.

Google AdInline article slot

Co se stane, když přidáš TTL na tabulku s 10 miliardami řádků? Nic hrozného. ClickHouse nebude přepisovat data okamžitě. Prostě začne TTL aplikovat na pozadí, postupně. Při příštím sloučení partů budou staré řádky vyřazeny.

3. TTL s přesunem na jiný disk (tiered storage)

Někdy je škoda data mazat, ale ukládat je na rychlých drahých SSD je nákladné. Řešení: přesouvat stará data na pomalé levné HDD (nebo do cloudového úložiště S3).

Nejprve nastavíme disky v konfiguraci ClickHouse (config.xml):

<storage_configuration>
    <disks>
        <ssd>
            <path>/mnt/ssd/clickhouse/</path>
        </ssd>
        <hdd>
            <path>/mnt/hdd/clickhouse/</path>
        </hdd>
    </disks>
    <policies>
        <hot_to_cold>
            <volumes>
                <hot>
                    <disk>ssd</disk>
                </hot>
                <cold>
                    <disk>hdd</disk>
                </cold>
            </volumes>
        </hot_to_cold>
    </policies>
</storage_configuration>

Nyní vytvoříme tabulku s TTL přesunem:

CREATE TABLE bets_tiered
(
    user_id     UInt64,
    amount      Decimal(18,2),
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 30 DAY TO DISK 'hdd';   -- Po 30 dnech se přesune na HDD

Co se stane: Řádky žijí na rychlém SSD prvních 30 dní (kdy jsou nejčastěji potřeba v reportech). Po 30 dnech ClickHouse na pozadí přesune party dat na HDD. Při dotazu na stará období budou data dostupná, ale o něco pomalejší.

Lze kombinovat – nejprve přesunout, pak smazat:

-- 30 dní na SSD, pak na HDD do 90 dní, pak smazat
CREATE TABLE bets_multi_ttl
(
    user_id UInt64,
    amount Decimal(18,2),
    created_at DateTime
)
ENGINE = MergeTree()
ORDER BY user_id
TTL
    created_at + INTERVAL 30 DAY TO DISK 'hdd',
    created_at + INTERVAL 90 DAY DELETE;

Analogický příklad: Hotel: prvních 30 dní bydlíš v luxusním apartmá (rychlé, drahé). Pak tě přestěhují do standardního pokoje (pomalejší, levnější). A po 90 dnech tě vystěhují. Vše automaticky.

4. TTL s přesunem na S3

ClickHouse umí pracovat s cloudovými úložišti (Amazon S3, MinIO, Google Cloud Storage). Můžeš nastavit storage_policy s S3 a přesouvat stará data do cloudu, kde je ukládání levné.

Nastavení (zjednodušeně):

<storage_configuration>
    <disks>
        <s3>
            <type>s3</type>
            <endpoint>https://s3.amazonaws.com/mybucket/clickhouse/</endpoint>
            <access_key_id>AKIAIOSFODNN7EXAMPLE</access_key_id>
            <secret_access_key>wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY</secret_access_key>
        </s3>
    </disks>
    <policies>
        <s3_policy>
            <volumes>
                <hot>
                    <disk>default</disk>   <!-- lokální SSD -->
                </hot>
                <cold_s3>
                    <disk>s3</disk>        <!-- cloud -->
                </cold_s3>
            </volumes>
        </s3_policy>
    </policies>
</storage_configuration>

Tabulka s TTL do S3:

CREATE TABLE bets_s3
(
    user_id UInt64,
    amount Decimal(18,2),
    created_at DateTime
)
ENGINE = MergeTree()
ORDER BY created_at
TTL created_at + INTERVAL 90 DAY TO VOLUME 'cold_s3';   -- po 90 dnech do S3

Proč je to skvělé: Platíš za S3 haléře za gigabajt měsíčně. Data zůstávají dostupná pro analýzu (i když pomaleji než z lokálního disku). Přitom se nemusíš starat o nedostatek místa na serveru – S3 je nekonečné.

5. TTL pro agregaci (nejsilnější funkce)

Toto je můj oblíbený vzor. Místo mazání starých detailních dat je sbalíš do agregátů. Například sázky starší 7 dní nepotřebuješ v rozlišení každé sekundy, ale potřebuješ součty po dnech a uživatelích.

-- Tabulka s detailními sázkami
CREATE TABLE bets_detailed
(
    user_id     UInt64,
    bet_id      String,
    amount      Decimal(18,2),
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 7 DAY
    GROUP BY toDate(created_at) AS day, user_id
    SET total_bets = sum(amount),           -- součet všech sázek za den
        bet_count = count()                 -- počet sázek za den
    DELETE WHERE day < now() - INTERVAL 90 DAY;   -- po 90 dnech smažeme i agregáty

Rozebíráme po částech:

  • TTL created_at + INTERVAL 7 DAY – po 7 dnech od vytvoření řádku (detailní sázky) přestává být detailní.
  • GROUP BY toDate(created_at) AS day, user_id – řádky se seskupí podle dne a uživatele. Místo tisíců detailních sázek za den pro jednoho uživatele bude jeden agregovaný řádek.
  • SET total_bets = sum(amount), bet_count = count() – v novém agregovaném řádku se pole vyplní agregačními funkcemi.
  • DELETE WHERE day < now() - INTERVAL 90 DAY – agregáty starší 90 dní se definitivně smažou.

Co se stane v praxi:

  1. Den 0–7: Data leží v detailní podobě. Můžeš analyzovat každou sázku.
  2. Den 7–90: Detailní sázky se sbalí do řádku na (user_id, day) s poli total_bets a bet_count. Místa je 10–100krát méně.
  3. Den 90+: I agregáty se smažou, zůstanou jen zálohy.

Analogický příklad: Píšeš si deník se záznamy každou hodinu. Po týdnu přepíšeš hodinové záznamy do denních souhrnů. A po třech měsících vyhodíš i denní souhrny a necháš jen měsíční reporty.

6. TTL pro konkrétní sloupce (GDPR anonymizace)

Podle zákona (GDPR v Evropě, osobní údaje) jsi povinen uchovávat osobní informace (e-mail, IP adresu) jen po určitou dobu. Anonymní analytiku (součet sázek, počet her) můžeš uchovávat navždy.

TTL lze aplikovat ne na celý řádek, ale na jednotlivé sloupce.

-- Tabulka s osobními údaji
CREATE TABLE user_events
(
    user_id     UInt64,
    email       String,           -- osobní sloupec
    ip_address  String,           -- osobní sloupec
    event_type  String,
    event_value UInt64,
    created_at  DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL
    email + INTERVAL 1 YEAR,      -- po roce se email vynuluje
    ip_address + INTERVAL 1 YEAR, -- po roce se IP vynuluje
    created_at + INTERVAL 10 YEAR; -- po 10 letech se smaže celý řádek

Co se stane: Rok po vytvoření řádku budou sloupce email a ip_address nahrazeny výchozími hodnotami (prázdný řetězec pro String, 0 pro čísla). Data zůstanou užitečná pro analýzu (víš, že existoval nějaký uživatel, ale nevíš kdo přesně). Po 10 letech se smaže celý řádek.

Proč je to důležité pro GDPR: Splňuješ požadavek zákona automaticky, bez ručních skriptů. Auditor může přijít, podívat se na tvá TTL pravidla a ujistit se, že osobní údaje nejsou uchovávány déle, než je povoleno.

7. Nastavení procesu na pozadí TTL

TTL nefunguje okamžitě po vypršení lhůty. ClickHouse spouští proces na pozadí, který:

  • kontroluje granule,
  • aplikuje TTL pravidla,
  • přepisuje party bez zastaralých dat nebo se změněnými sloupci.

Parametry nastavení (v config.xml nebo pomocí SET):

-- Interval mezi spuštěními TTL procesu (výchozí 1 den)
ALTER SYSTEM MODIFY SETTING merge_with_ttl_timeout = 86400;  -- v sekundách

-- Pro testování lze nastavit častěji
SET merge_with_ttl_timeout = 3600;   -- každou hodinu

Proč by TTL nemělo být příliš časté? Protože aplikace TTL je operace přepisování partů dat, zatěžuje CPU a disky. Každý den je normální. Každou hodinu už může vadit, pokud máš terabajty dat.

Jak zkontrolovat, že TTL funguje:

-- Podíváme se, pro které party je TTL aktivní
SELECT 
    partition,
    name,
    rows,
    modification_time,
    has_ttl_info   -- 1 = na tento part byl aplikován TTL
FROM system.parts
WHERE table = 'bets' AND active = 1;

8. MATERIALIZE TTL – vynucená aplikace

Někdy je potřeba, aby se TTL aplikovalo okamžitě, nečekat na proces na pozadí. Například:

  • Právě jsi přidal TTL na obrovskou tabulku a chceš ihned vyčistit stará data.
  • Testuješ TTL pravidla a nechceš čekat den.
-- Vynuceně aplikujeme TTL na celou tabulku
ALTER TABLE bets MATERIALIZE TTL;

-- Pouze pro konkrétní partition (rychlejší)
ALTER TABLE bets MATERIALIZE TTL IN PARTITION '202501';

Co se stane: ClickHouse prohledá všechny party tabulky (nebo partition) a okamžitě aplikuje všechna TTL pravidla. Může to trvat minuty nebo hodiny u velkých tabulek. Nedělej to ve špičce.

Kdy použít: V noci, během údržby nebo před vytvořením zálohy, aby se nezálohovala zbytečně mrtvá data.

9. Reálný use case: casino s GDPR a agregáty

Teď to dáme všechno dohromady. Představ si kompletní schéma ukládání událostí v online casinu.

-- Surové události (každá sázka, každá hra)
CREATE TABLE raw_events
(
    user_id         UInt64,
    session_id      String,
    ip_address      String,           -- GDPR-sensitive
    event_type      String,           -- 'bet', 'win', 'login'
    event_value     Int64,
    created_at      DateTime
)
ENGINE = MergeTree()
ORDER BY (user_id, created_at)
TTL
    -- Detailní události uchováváme 30 dní
    created_at + INTERVAL 30 DAY DELETE,
    -- IP adresu mažeme po 14 dnech (GDPR)
    ip_address + INTERVAL 14 DAY,
    -- Agregace pro stará data: po 30 dnech sbalíme do denních souhrnů
    created_at + INTERVAL 30 DAY
        GROUP BY toDate(created_at) AS day, user_id
        SET total_bets = sumIf(event_value, event_type = 'bet'),
            total_wins = sumIf(event_value, event_type = 'win'),
            sessions_count = countDistinct(session_id)
        DELETE WHERE day < now() - INTERVAL 2 YEAR;   -- agregáty uchováváme 2 roky

Co jsme získali:

  • 0–14 dní: Plná informace včetně IP adres. Lze obnovovat incidenty, odhalovat multiaccounting.
  • 15–30 dní: IP adresy jsou již vynulovány (anonymizovány), ale detailní události stále existují. Lze analyzovat chování uživatelů bez vazby na geolokaci.
  • 31 dní – 2 roky: Detailní události smazány. Místo nich agregované řádky na (den, uživatel). Místa je 100krát méně. Dashboardy fungují rychle.
  • Starší než 2 roky: Vše smazáno. Pouze zálohy na S3 (pokud je děláš).

Jak číst agregovaná data po TTL:

-- Nyní je v tabulce mix: detailní řádky (prvních 30 dní) a agregáty (do 2 let)
-- Stejně píšeš běžnou agregaci, která funguje na oba typy řádků
SELECT 
    toDate(created_at) AS day,
    user_id,
    sum(event_value) AS total
FROM raw_events
WHERE created_at >= today() - 45
GROUP BY day, user_id;

ClickHouse si sám poradí: pro čerstvá data sečte detailní řádky, pro stará použije hotové agregáty. Magie.

10. Kdy TTL NEPOUŽÍVAT a alternativy

Kdy TTL není vhodné:

  • Data se často aktualizují. TTL se spouští při vložení, ne při poslední aktualizaci. Pokud měníš řádek pomocí INSERT se zrušením (CollapsingMergeTree), TTL bude počítat od původního created_at. Řešení: použít sloupec updated_at ve výrazu TTL.

  • Potřebuješ přesné řízení času. TTL proces je na pozadí a není přesný. Pokud potřebuješ zaručeně smazat řádek přesně o půlnoci – nepůjde. Rozptyl může být až několik hodin.

  • Velmi velké tabulky s řídkými mergemi. TTL se aplikuje během slučování partů. Pokud se tabulka slučuje zřídka (např. kvůli nastavení merge_with_ttl_timeout), stará data mohou viset déle.

Alternativy k TTL:

Přístup Kdy použít Výhody Nevýhody
Partitionování + DROP PARTITION Data přicházejí kontinuálně, mazání podle kalendáře Okamžité smazání, žádná režie Potřebuje externí plánovač, není flexibilní (jen podle partition)
TTL DELETE Data mohou přicházet se zpožděním, mazání podle stáří řádku Vestavěné, flexibilní, nevyžaduje externí skripty Nepřesný čas mazání, zátěž na merge
TTL TO DISK Potřeba uchovat data, ale na levném médiu Úspora úložiště, transparentní pro dotazy Vyžaduje nastavení storage_policy
TTL GROUP BY Detailní data nejsou potřeba, jen agregáty Výrazné snížení místa (100+ krát) Ztráta detailů, složitější ladění

Kombinuj s partitionováním pro lepší výsledky

Nejlepší praxe: používej partitionování + TTL dohromady.

CREATE TABLE bets_optimized
(
    user_id UInt64,
    amount Decimal(18,2),
    created_at DateTime
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)           -- partitiony po měsících
ORDER BY (user_id, created_at)
TTL created_at + INTERVAL 30 DAY DELETE;    -- TTL po 30 dnech

Proč je to dobré:

  • Partitiony pomáhají rychle mazat celé měsíce (pokud TTL zaostává).
  • TTL čistí uvnitř partition flexibilněji (podle stáří řádku, ne podle kalendáře).

Co dál

Nyní ovládáš všechny nástroje automatického řízení dat v ClickHouse. Další témata:

  • Pokročilé storage policies – jak nastavit automatický přesun z SSD → HDD → S3 s různými TTL v každé fázi.
  • Monitorování TTL – jak pomocí system.query_log sledovat, kolik dat se maže a jak rychle.
  • TTL + Materialized Views – jak automaticky agregovat stará data do samostatné tabulky, aniž by se mísila s detailními.

Shrnutí: TTL v ClickHouse je švýcarský nůž pro řízení životního cyklu dat. Umí mazat, přesouvat, agregovat a anonymizovat. Používej TTL ... DELETE pro čištění, TTL ... TO DISK pro úsporu, TTL ... GROUP BY pro magii s agregáty a TTL na sloupce pro GDPR. A nezapomínej na MATERIALIZE TTL, když potřebuješ pravidla aplikovat okamžitě.


Předchozí:
Další: Slovníky v ClickHouse: rychlý lookup bez JOIN

— Editorial Team

Advertisement 728x90

Číst dál