Partitions and TTL

ClickHouse · Practical · 12 min

Retention and cold storage are routine for event data, and partitions make them nearly free.

PARTITION BY splits a MergeTree table into independent groups of parts; parts from different partitions are never merged together. In ClickHouse partitioning is mainly a data management tool, not a speed trick; the sort key does the speed work.

What partitions give you:

  • ALTER TABLE t DROP PARTITION 202401 deletes a month instantly, without rewriting anything.
  • DETACH/ATTACH PARTITION, MOVE PARTITION ... TO TABLE, and REPLACE PARTITION for backfills and swaps.
  • Pruning: queries filtering on the partition expression skip whole partitions, as a bonus.

Keep partitions coarse. toYYYYMM(ts) (monthly) is the usual choice; daily can be fine for very large tables. Partitioning by a high-cardinality column such as user id creates huge numbers of parts and triggers "Too many parts" errors, because every insert touching N partitions creates N parts.

TTL expires data automatically during merges:

  • Table TTL: TTL ts + INTERVAL 90 DAY deletes rows older than 90 days.
  • Column TTL resets a column to its default after a time (drop raw payloads, keep counters).
  • TTL ts + INTERVAL 30 DAY TO VOLUME 'cold' moves data to cheaper disks in a tiered storage policy.

When a TTL lines up with partitions, the setting ttl_only_drop_parts = 1 lets ClickHouse drop whole expired parts instead of rewriting them, which is much cheaper.

Example

CREATE TABLE metrics
(
    ts     DateTime,
    host   LowCardinality(String),
    value  Float64,
    raw    String TTL ts + INTERVAL 7 DAY        -- column TTL: clear after a week
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(ts)
ORDER BY (host, ts)
TTL ts + INTERVAL 90 DAY                         -- delete rows after 90 days
SETTINGS ttl_only_drop_parts = 1;

INSERT INTO metrics VALUES ('2026-08-15 10:00:00', 'a', 1, 'x'), ('2026-09-15 10:00:00', 'a', 2, 'y');

SELECT partition, sum(rows) FROM system.parts
WHERE table = 'metrics' AND active GROUP BY partition ORDER BY partition;
-- 202608  1
-- 202609  1      (until the 90-day TTL removes them)

ALTER TABLE metrics DROP PARTITION 202608;       -- instant month delete

Remember

  • Partitions are for lifecycle: drop, move, replace whole chunks cheaply
  • Keep partitions coarse, typically monthly
  • TTL deletes or moves rows during merges; columns can have their own TTL

Watch out for

  • Partitioning by a high-cardinality column, which explodes the number of parts.

Check yourself

  1. What is the main purpose of PARTITION BY in ClickHouse?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: clickhouse.com/docs/engines/table-engines/mergetree-family/custom-partitioning-key