Playbooks

Schema & compression

The cheapest byte is the one you never store. ClickHouse gives you per-column codecs, LowCardinality, skipping indexes and projections — but defaults rarely use them well. This playbook shows how to find columns and tables paying a hidden tax, and the exact DDL to fix them.

Symptoms

  • Tables are larger on disk than the data really warrants.
  • Low compression ratios on numeric, timestamp or repetitive string columns.
  • Filters on non-primary-key columns force full-table scans.
  • Common aggregations re-read the whole table every time.

Step 1 — Find poorly compressed columns

`system.columns` exposes compressed and uncompressed bytes per column. A low ratio means the column is a codec candidate — numeric and timestamp columns especially can compress 2–5× better with the right codec.

sql
SELECT
    table,
    name,
    formatReadableSize(sum(data_compressed_bytes))   AS comp,
    formatReadableSize(sum(data_uncompressed_bytes)) AS uncomp,
    round(sum(data_uncompressed_bytes)
        / sum(data_compressed_bytes), 1)             AS ratio
FROM system.columns
WHERE database = currentDatabase()
  AND data_compressed_bytes > 0
GROUP BY table, name
ORDER BY sum(data_compressed_bytes) DESC
LIMIT 30;

Pick the right codec

Column shapeCodecWhy
Monotonic / slowly-changing (timestamps, IDs)Delta, ZSTDDelta stores differences; ZSTD then crushes the small deltas.
Gauge-style floats (metrics)Gorilla, ZSTDGorilla is built for time-series float sequences.
Wide integers with small rangeT64, ZSTDT64 transposes bits so ZSTD finds the redundancy.
Large text / JSON blobsZSTD(3)Higher ZSTD level trades a little CPU for big size wins.
sql
-- Apply a codec to an existing column (rewrites on next merge)
ALTER TABLE events
    MODIFY COLUMN created_at DateTime CODEC(Delta, ZSTD);

ALTER TABLE metrics
    MODIFY COLUMN value Float64 CODEC(Gorilla, ZSTD);

Step 2 — LowCardinality for repetitive strings

A string column with few distinct values (status, country, plan, source) stored as plain String repeats the same text on every row. LowCardinality dictionary-encodes it, shrinking storage and speeding up GROUP BY and filters.

sql
-- Check distinct count first — LowCardinality wins under ~100k uniques
SELECT uniqExact(status) FROM events;

ALTER TABLE events
    MODIFY COLUMN status LowCardinality(String);

Step 3 — Skipping indexes for non-PK filters

When you frequently filter on a column that is not in the primary key, a data-skipping index lets ClickHouse skip granules that cannot match — without a full scan.

sql
-- minmax for range filters, bloom_filter for equality on high-cardinality
ALTER TABLE events
    ADD INDEX idx_user user_id TYPE bloom_filter GRANULARITY 4;

ALTER TABLE events
    ADD INDEX idx_amount amount TYPE minmax GRANULARITY 4;

-- Build it on existing data
ALTER TABLE events MATERIALIZE INDEX idx_user;

Step 4 — Projections for hot read patterns

If one aggregation or a different sort order is read constantly, a projection stores a pre-sorted / pre-aggregated copy inside the table. ClickHouse picks it automatically when it serves the query.

sql
ALTER TABLE events
    ADD PROJECTION p_by_user
    (
        SELECT user_id, count(), sum(amount)
        GROUP BY user_id
    );

ALTER TABLE events MATERIALIZE PROJECTION p_by_user;
Primary key order matters most
Before adding indexes, check the ORDER BY of the table. The primary key should lead with the lowest-cardinality columns you filter on most — a wrong order costs more than any codec can recover.

How OptiHouse automates this

Per-column codec advisor (7.1)
Finds poorly-compressed columns and hands you the exact ALTER ... CODEC to shrink them 2–5×.
LowCardinality / Enum advisor (7.2)
String columns with few distinct values worth dictionary-encoding.
Skipping index recommender (7.3)
Filter columns that would benefit from a minmax, set or bloom_filter index.
Projection advisor (7.4)
Read patterns a projection inside the source table would accelerate.
PK reorder & type downsizing (7.5)
Primary-key column order and oversized column types worth revisiting.
Every advisor ships the DDL
OptiHouse does not just say a column is poorly compressed — it generates the precise ALTER statement, estimates the saving, and (where you allow it) can apply and verify the change.