ClickHouse Performance Tuning
Overview
Diagnose and fix ClickHouse performance issues using query analysis, proper indexing,
projections, materialized views, and server settings tuning. Work top-down: measure
first with system.query_log, then apply the single highest-leverage fix (usually the
ORDER BY key), then re-measure to confirm.
Prerequisites
- ClickHouse tables with data (see
clickhouse-core-workflow-a)
- Access to
system.query_log and system.parts
Instructions
The tuning workflow is seven independent steps. Diagnose first, then reach for the fix
that matches the bottleneck. Each step's full SQL lives in
references/implementation.md — start there for the
complete, copy-paste commands.
- Diagnose slow queries — rank the last 24h of
system.query_log by
query_duration_ms, then inspect a suspect query with EXPLAIN PLAN /
EXPLAIN PIPELINE.
- ORDER BY key optimization — the primary lever. Filtering on the ORDER BY prefix
skips whole granules; a mismatched key forces a full scan.
- Data skipping indexes —
bloom_filter for high-cardinality lookups, set for
low-cardinality columns, minmax for range filters on non-key columns.
- Projections — automatic pre-aggregation ClickHouse picks transparently when a
query matches the projection's shape.
- Server settings —
max_threads, external sort/group-by spill, async_insert,
and friends, set per-query or per-session.
- Materialized views — pre-aggregate on INSERT into an
AggregatingMergeTree so
dashboard reads hit milliseconds, not seconds.
- Query patterns —
PREWHERE, LIMIT BY, and avoiding FINAL.
The essential first move — find the slowest queries:
SELECT event_time, query_duration_ms, read_rows, read_bytes,
substring(query, 1, 300) AS query_preview
FROM system.query_log
WHERE type = 'QueryFinish'
AND event_time >= now() - INTERVAL 24 HOUR
AND query_duration_ms > 1000 -- > 1 second
ORDER BY query_duration_ms DESC
LIMIT 20;
Output
Applying this workflow produces:
- A ranked list of the slowest queries with their
read_rows / read_bytes cost.
- One or more concrete schema/query changes: a corrected
ORDER BY key, added data
skipping indexes, a projection, a materialized view, or tuned session settings.
- A before/after measurement from
system.query_log proving the change reduced
read_rows, read_bytes, query_duration_ms, or memory_usage.
Error Handling
| Issue |
Indicator |
Solution |
| Full table scan |
read_rows = total rows |
Fix ORDER BY to match filters |
| Memory exceeded |
Error 241 |
Add LIMIT, use streaming, increase limit |
| Slow GROUP BY |
High read_bytes |
Add materialized view or projection |
| Merge backlog |
Parts > 300 |
Reduce insert frequency, increase merge threads |
Examples
Worked before/after scenarios — full-scan → ORDER BY fix, slow GROUP BY → projection,
confirming a skipping index fires, and the query-cost measurement query — are in
references/examples.md. The core measurement, run right after
any query you are tuning:
SELECT query_duration_ms, read_rows,
formatReadableSize(read_bytes) AS read_size,
formatReadableSize(memory_usage) AS memory
FROM system.query_log
WHERE query_id = currentQueryId() AND type = 'QueryFinish';
Resources
Next Steps
For cost optimization, see clickhouse-cost-tuning.
Source: jeremylongshore/claude-code-plugins-plus-skills → plugins/saas-packs/clickhouse-pack/skills/clickhouse-performance-tuning/SKILL.md
1---2name: clickhouse-performance-tuning3description: | Optimize ClickHouse query performance with indexing, projections, settings tuning, and query analysis using system tables. Use when queries are slow, investigating performance bottlenecks, or tuning ClickHouse server settings. Trigger with "clickhouse performance", "optimize clickhouse query", "clickhouse slow query", "clickhouse indexing", "clickhouse tuning", "clickhouse projections".4---56# ClickHouse Performance Tuning78## Overview910Diagnose and fix ClickHouse performance issues using query analysis, proper indexing,11projections, materialized views, and server settings tuning. Work top-down: measure12first with `system.query_log`, then apply the single highest-leverage fix (usually the13ORDER BY key), then re-measure to confirm.1415## Prerequisites1617- ClickHouse tables with data (see `clickhouse-core-workflow-a`)18- Access to `system.query_log` and `system.parts`1920## Instructions2122The tuning workflow is seven independent steps. Diagnose first, then reach for the fix23that matches the bottleneck. Each step's full SQL lives in24[references/implementation.md](references/implementation.md) — start there for the25complete, copy-paste commands.26271. **Diagnose slow queries** — rank the last 24h of `system.query_log` by28 `query_duration_ms`, then inspect a suspect query with `EXPLAIN PLAN` /29 `EXPLAIN PIPELINE`.302. **ORDER BY key optimization** — the primary lever. Filtering on the ORDER BY prefix31 skips whole granules; a mismatched key forces a full scan.323. **Data skipping indexes** — `bloom_filter` for high-cardinality lookups, `set` for33 low-cardinality columns, `minmax` for range filters on non-key columns.344. **Projections** — automatic pre-aggregation ClickHouse picks transparently when a35 query matches the projection's shape.365. **Server settings** — `max_threads`, external sort/group-by spill, `async_insert`,37 and friends, set per-query or per-session.386. **Materialized views** — pre-aggregate on INSERT into an `AggregatingMergeTree` so39 dashboard reads hit milliseconds, not seconds.407. **Query patterns** — `PREWHERE`, `LIMIT BY`, and avoiding `FINAL`.4142The essential first move — find the slowest queries:4344```sql45SELECT event_time, query_duration_ms, read_rows, read_bytes,46 substring(query, 1, 300) AS query_preview47FROM system.query_log48WHERE type = 'QueryFinish'49 AND event_time >= now() - INTERVAL 24 HOUR50 AND query_duration_ms > 1000 -- > 1 second51ORDER BY query_duration_ms DESC52LIMIT 20;53```5455## Output5657Applying this workflow produces:5859- A ranked list of the slowest queries with their `read_rows` / `read_bytes` cost.60- One or more concrete schema/query changes: a corrected `ORDER BY` key, added data61 skipping indexes, a projection, a materialized view, or tuned session settings.62- A before/after measurement from `system.query_log` proving the change reduced63 `read_rows`, `read_bytes`, `query_duration_ms`, or `memory_usage`.6465## Error Handling6667| Issue | Indicator | Solution |68|-------|-----------|----------|69| Full table scan | `read_rows` = total rows | Fix ORDER BY to match filters |70| Memory exceeded | Error 241 | Add LIMIT, use streaming, increase limit |71| Slow GROUP BY | High `read_bytes` | Add materialized view or projection |72| Merge backlog | Parts > 300 | Reduce insert frequency, increase merge threads |7374## Examples7576Worked before/after scenarios — full-scan → ORDER BY fix, slow GROUP BY → projection,77confirming a skipping index fires, and the query-cost measurement query — are in78[references/examples.md](references/examples.md). The core measurement, run right after79any query you are tuning:8081```sql82SELECT query_duration_ms, read_rows,83 formatReadableSize(read_bytes) AS read_size,84 formatReadableSize(memory_usage) AS memory85FROM system.query_log86WHERE query_id = currentQueryId() AND type = 'QueryFinish';87```8889## Resources9091- [references/implementation.md](references/implementation.md) — full 7-step SQL walkthrough92- [references/examples.md](references/examples.md) — worked before/after tuning examples93- [Projections](https://clickhouse.com/docs/sql-reference/statements/alter/projection)94- [Data Skipping Indexes](https://clickhouse.com/docs/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-data_skipping-indexes)95- [MergeTree Settings](https://clickhouse.com/docs/operations/settings/merge-tree-settings)9697## Next Steps9899For cost optimization, see `clickhouse-cost-tuning`.100101---102103**Source:** [`jeremylongshore/claude-code-plugins-plus-skills`](https://github.com/jeremylongshore/claude-code-plugins-plus-skills) → `plugins/saas-packs/clickhouse-pack/skills/clickhouse-performance-tuning/SKILL.md`