Design canvas — pick a workload, ship a schema
Every column-store knob — sort key, partition, encoding, skip index, MV, denormalization — is a deliberate choice driven by the workload shape, and the right schema for observability is wrong for BI which is wrong for event analytics.
We've now seen every lever — layout, encoding, execution, parts, merge, sort key, sparse index, skip index, MVs, denormalization. The last move is to face a real workload and pick the configuration end-to-end.
Scene 13
Design canvas: pick a workload, ship a schema
- Watch
- Try it
- Predict
- Capture
Workload A — Observability. Defaults are pre-loaded: ORDER BY (service, ts), partition monthly. The verifier walks green and the chips trace every knob back to an earlier scene. This is what twelve scenes of column-store thinking cash out to.
Highlighted lines are the ones running in the diagram right now.
CREATE TABLE events (service LowCardinality(String),host LowCardinality(String),ts DateTime CODEC(Delta, ZSTD),trace_id String,metric Float64,INDEX idx_host host TYPE set(100) GRANULARITY 4,INDEX idx_trace trace_id TYPE bloom_filter GRANULARITY 4) ENGINE = MergeTreeORDER BY (service, ts)PARTITION BY toYYYYMM(ts)SETTINGS index_granularity = 8192;
CREATE TABLE events (service LowCardinality(String),region LowCardinality(String),ts DateTime CODEC(Delta, ZSTD),customer_id UInt64,revenue Decimal(18, 2)) ENGINE = MergeTreeORDER BY (service, ts)PARTITION BY toYYYYMM(ts);CREATE MATERIALIZED VIEW daily_revenue_mvENGINE = AggregatingMergeTreeORDER BY (toDate(ts), region) ASSELECT toDate(ts) AS d, region,sumState(revenue) AS revenue_stateFROM events GROUP BY d, region;
CREATE TABLE inventory (sku_id UInt64,qty Int32,price Decimal(18, 2),updated DateTime) ENGINE = MergeTreeORDER BY (sku_id)PARTITION BY toYYYYMM(updated);-- Per-row UPDATE arrives from the OLTP app:ALTER TABLE inventory UPDATE qty = qty - 1WHERE sku_id = 42; -- rewrites every part touching `qty`-- Verifier REFUSES: parts are immutable; one new part per write.-- Merges cannot keep up → 'too many parts' fires in minutes.-- Right answer: use Postgres, or stage in PG and CDC to ClickHouse.
CREATE TABLE events_bad (service LowCardinality(String),ts DateTime,metric Float64) ENGINE = MergeTreeORDER BY (ts)-- Leading with ts: every WHERE service=... scans every-- granule; sparse index prunes nothing.PARTITION BY toStartOfHour(ts);-- Hourly × shards → tiny-part explosion within minutes;-- INSERTs throw 'too many parts', merges never catch up.
Where this sits in Build a columnar OLAP store (ClickHouse / Druid style)
Scene 13 of 13, in the Design canvas act — Pick the workload, configure the engine, watch each failure mode cite its scene.. Every knob — sort key, partition, skip index, MV, denormalization — is a workload-shaped choice. The right observability schema is wrong for BI.
All 13 scenes in Build a columnar OLAP store (ClickHouse / Druid style) · Every curriculum