ChistaDATA Inc.

Enterprise-class 24*7 ClickHouse Consultative Support and Managed Services

  • ChistaDATA
    • ClickHouse®
    • ClickHouse MergeTree
    • Why is ClickHouse So Fast
    • Columnar Stores
    • Vectorized Query
    • For CTOs
  • Engineering
    • Real-Time Analytics
    • Break Fix Engineering
    • Data Foundation
    • Data Archiving
    • Cloud Native ClickHouse
    • ClickHouse Consulting
      • Performance Audit
        • Pre- Engagement Questionnaire
    • ClickHouse Strategy
    • Online Ticketing System
  • Support
    • ClickHouse Migration
    • ClickHouse Audit
    • Data Warehousing Support
    • Data Analytics
    • Gen AI
    • Online Ticketing System
  • ClickHouse Managed Services
    • ClickHouse DBA
    • ClickHouse Performance
    • Data Strategy
    • ClickHouse Analytics
    • Data Archiving
    • DBaaS Optimization
    • Data SRE
    • Online Ticketing System
  • Blog
    • ChistaDATA Blog
  • University
  • Careers
  • Contact
  • Twitter
  • Facebook
  • LinkedIn
    • Shiv Iyer
  • GitHub
    • @ShivIyer
HomeClickHouse Performance

ClickHouse Performance

ClickHouse performance work, done properly, is a review with a fixed order of questions and a measurement behind every answer. It starts from the workload as system.query_log records it, moves to the storage layout that the workload runs against, then to the resources the node has to give it, and only at the end to settings.

Most performance problems on ClickHouse are found in the first two steps and fixed with a sort key, a partition scheme or a batching change; settings are last because they are the smallest lever and the easiest to over-turn. This page is that review, written as the ten questions asked in order during a ClickHouse performance audit, with the query or table that answers each and the threshold that decides whether to move on.

This is the largest category in the archive, with more than three hundred posts covering every one of the ten questions in depth. The review below is the spine; the linked posts are the muscle.

Question 1: what is the ClickHouse performance workload, in the words of system.query_log?

A ClickHouse performance review that starts from a slow query someone noticed is a review of one query. Starting from the log is a review of the system. system.query_log with normalized_query_hash groups every statement by shape, and the first table produced is the top twenty shapes by total time, with their count, p95 and read volume. That table decides where the rest of the review goes: a system whose time is dominated by three dashboard queries is tuned differently from one where ten thousand distinct ad-hoc queries share the load evenly.

SELECT
    normalized_query_hash                              AS shape,
    count()                                            AS runs,
    round(sum(query_duration_ms) / 1000)               AS total_s,
    quantile(0.95)(query_duration_ms)                  AS p95_ms,
    formatReadableSize(avg(read_bytes))                AS avg_read,
    round(avg(read_rows) / greatest(avg(result_rows), 1)) AS rows_read_per_result_row,
    any(substring(query, 1, 120))                      AS sample
FROM system.query_log
WHERE type = 'QueryFinish'
  AND query_kind = 'Select'
  AND event_time > now() - INTERVAL 7 DAY
GROUP BY shape
ORDER BY total_s DESC
LIMIT 20;

The column that matters most is rows_read_per_result_row. A shape that reads a million rows to return ten is a pruning problem (question 3); one that reads ten rows per result row and is still slow is a compute or concurrency problem (questions 6 and 8). The archive posts on mining query_log for performance and the performance audit guide go deeper on this first table.

Question 2: are inserts shaped for the part model?

The same log answers the write-side question. ClickHouse performance degrades faster from small inserts than from any query pattern, because every insert is a part and every part is a merge. Average rows per INSERT below ten thousand, or more than a few inserts per second per table, means the merge pool is working to undo the ingestion pattern. The threshold used in reviews is simple: if the merge pool is busy more than half the time (system.metrics.BackgroundMergesAndMutationsPoolTask against background_pool_size), the insert shape is the first fix regardless of what the queries look like.

SELECT
    tables[1]                        AS table,
    count()                          AS inserts,
    round(avg(written_rows))         AS avg_rows,
    round(count() / (7 * 86400), 2)  AS inserts_per_second
FROM system.query_log
WHERE type = 'QueryFinish'
  AND query_kind = 'Insert'
  AND event_time > now() - INTERVAL 7 DAY
GROUP BY table
ORDER BY inserts DESC
LIMIT 10;

The fix is batching at the client, async_insert on the server (stable since 22.x), or a Buffer or Kafka engine in front of the table; the ingestion performance post compares the three.

Question 3: does the sort key match the predicates? Most ClickHouse performance lives here

This is where most ClickHouse performance is won or lost, and the evidence is EXPLAIN indexes = 1 on the top shapes from question 1. The PrimaryKey line shows how many granules survive the sort key; a shape that keeps more than a few percent of granules on a filtered query has a sort key that does not lead with its predicate columns. The review records, for each top shape, the granules read against the granules in the table, and the ones above 5 percent go on the list for a sort-key change, a projection, or a materialized view.

EXPLAIN indexes = 1
SELECT count()
FROM events
WHERE event_date >= today() - 7
  AND tenant_id = 42;
-- PrimaryKey Keys: event_date  Parts: 7/120  Granules: 41000/61440
-- 67% of granules kept: tenant_id is not in the key

A sort-key change on a large table is a rebuild (create the new table, INSERT ... SELECT by partition, swap), so the review also checks whether a projection with the alternate order (ALTER TABLE ... ADD PROJECTION, stable since 22.x) serves the shape without touching the base table. The posts on partition versus primary-key pruning and MergeTree optimisation cover the decision.

Question 4: how much of the read is I/O, and from where?

Once pruning is right, the review measures what the surviving granules cost to read. ProfileEvents in system.query_log separates bytes read from the page cache, from local disk and from object storage, and the ratio of OSReadBytes (physical) to ReadCompressedBytes (logical) shows how much the page cache is helping. On a node where physical reads approach logical reads during the working day, the working set does not fit in RAM and ClickHouse performance is bounded by the storage tier, which moves the conversation to compression (smaller bytes), tiering (hot data on faster disks) or memory (a bigger page cache).

SELECT
    normalized_query_hash                                       AS shape,
    formatReadableSize(sum(ProfileEvents['ReadCompressedBytes'])) AS logical,
    formatReadableSize(sum(ProfileEvents['OSReadBytes']))         AS physical,
    round(sum(ProfileEvents['OSReadBytes'])
        / greatest(sum(ProfileEvents['ReadCompressedBytes']), 1), 2) AS physical_ratio,
    formatReadableSize(sum(ProfileEvents['ReadBufferFromS3Bytes'])) AS from_s3
FROM system.query_log
WHERE type = 'QueryFinish'
  AND event_time > now() - INTERVAL 1 DAY
GROUP BY shape
ORDER BY sum(ProfileEvents['OSReadBytes']) DESC
LIMIT 10;

The IOPS troubleshooting post and the performance and I/O hub take this question through disk selection and queue depth.

Question 5: are parts and merges healthy?

Parts per partition, merge duration and mutation backlog are the storage-layer health signals, and a ClickHouse performance review reads them before touching any query. More than a few hundred active parts in a partition means every query over that partition opens that many files per column; a merge that has run for an hour is either enormous or starved; a mutation older than a day is a stuck ALTER. The three queries are the ones the DBA script hub runs every five minutes, and the review takes their output as-is.

SELECT
    table,
    partition,
    count()                              AS parts,
    formatReadableSize(sum(bytes_on_disk)) AS on_disk,
    max(modification_time)               AS newest_part
FROM system.parts
WHERE active
GROUP BY table, partition
HAVING parts > 100
ORDER BY parts DESC;

The merge and mutation performance post is the detailed reference for what to do when the counts are wrong.

Question 6: where does the CPU go?

For shapes that read little and still run long, the profiler answers. system.trace_log with query_profiler_cpu_time_period_ns set (a sampling period of 10 ms is safe on production) records stack samples per query, and aggregating them by top frame shows whether the time is in decompression, hashing for GROUP BY, string functions, or waiting on a lock. This is the step that finds the ILIKE on a hot path, the JSONExtract called per row on a String column, and the aggregation over a high-cardinality key that should be pre-aggregated in a materialized view.

SET query_profiler_cpu_time_period_ns = 10000000;   -- 10 ms

SELECT
    arrayStringConcat(arrayMap(x -> demangle(addressToSymbol(x)), trace), '\n') AS stack,
    count()                                                                      AS samples
FROM system.trace_log
WHERE trace_type = 'CPU'
  AND query_id = '${QUERY_ID}'
GROUP BY trace
ORDER BY samples DESC
LIMIT 5
SETTINGS allow_introspection_functions = 1;

The eBPF performance analysis post extends the same method below the process boundary, and the CPU efficiency post covers the settings that follow from what the profile shows.

Question 7: is memory the real limit?

ClickHouse performance and ClickHouse memory are the same conversation once a query spills or fails.

The review looks at memory_usage per shape in system.query_log, the count of MEMORY_LIMIT_EXCEEDED exceptions, and whether max_bytes_before_external_group_by and max_bytes_before_external_sort are set, because a query that spills to disk is slow but finishes, while one that hits the limit fails and is retried by the client, which is worse for everyone else.

The memory hub has the seven-layer model; the review’s job is to record which shapes are memory-bound and whether the fix is a smaller hash table (pre-aggregation), a spill, or a bigger node.

Question 8: what does concurrency do to ClickHouse performance at the p99?

Every measurement so far was per query. Production is many at once, and ClickHouse performance under concurrency is governed by max_threads per query multiplied by concurrent queries against the core count. A 32-core node running 20 queries at max_threads = 32 is oversubscribed twenty times, and the p99 reflects the scheduler, not the queries.

The review plots concurrent query count (system.metrics.Query, sampled) against p99 from the log and looks for the knee; above the knee the fix is a lower max_threads for the dashboard user, workload scheduling (SETTINGS workload, since 23.x), or a separate replica for the analytical load.

SELECT
    toStartOfFiveMinutes(event_time)              AS t,
    count()                                       AS queries,
    quantile(0.99)(query_duration_ms)             AS p99_ms,
    max(ProfileEvents['ConcurrentQueries'])       AS peak_concurrent  -- illustrative; sample system.metrics for the real series
FROM system.query_log
WHERE type = 'QueryFinish'
  AND event_time > now() - INTERVAL 1 DAY
GROUP BY t
ORDER BY p99_ms DESC
LIMIT 12;

The posts on thread contention and designing for mixed workloads cover the two sides of this question.

Question 9: is the cluster topology helping or hurting?

On a sharded cluster, ClickHouse performance adds a network stage: the initiator fans the query out, each shard reads and partially aggregates, and the initiator merges. Two things go wrong. A query that should hit one shard (by sharding key) hits all of them, multiplying work; and the final merge on the initiator becomes the bottleneck for high-cardinality GROUP BY. EXPLAIN on the distributed table and system.query_log on each shard (the same initial_query_id) show both. The sharding troubleshooting and hot-spot detection posts are the references, and the sharding archive has the design side.

Question 10: ClickHouse performance settings, and only now

Settings come last because by this point the review knows which of the nine earlier questions the setting is meant to answer. The performance settings for 26.8 LTS post lists the current defaults; the review’s rule is to change one setting at a time, on one replica, with the p95 of the affected shapes recorded before and after, and to keep the change only if the number moved. Settings that are almost always worth reviewing: max_threads per profile, max_memory_usage per profile, max_bytes_before_external_group_by, use_uncompressed_cache (usually off), mark_cache_size (usually up), and background_pool_size (rarely up without more cores).

QuestionEvidenceThreshold that triggers actionTypical fix
1. Workloadquery_log by shaperows read per result row > 1,000go to 3
2. Insertsquery_log INSERT< 10,000 rows per insertbatch, async_insert
3. PruningEXPLAIN indexes = 1> 5% granules kept on filtered shapessort key, projection, MV
4. I/OProfileEvents OSReadBytesphysical ratio > 0.5 in the working daycompression, tiering, RAM
5. Partssystem.parts, merges, mutations> 100 parts per partitionfix 2, partition scheme
6. CPUtrace_log samplesone frame > 40% of samplesrewrite function, MV
7. Memorymemory_usage, exceptionsany MEMORY_LIMIT_EXCEEDEDspill settings, pre-aggregate
8. Concurrencyp99 vs concurrent countp99 knee below peak loadmax_threads, workloads, replica
9. Topologyper-shard query_logall shards hit for keyed queriessharding key, routing
10. Settingsbefore/after p95no movement = revertone at a time
ClickHouse performance review diagram: ten questions in order from workload and inserts through pruning, I/O, parts, CPU, memory, concurrency and topology to settings, each with its evidence and threshold
The ten-question order of a ClickHouse performance review. Thresholds are starting points, tuned per cluster.

What a ClickHouse performance review delivers

The output is not a list of settings. It is the question-1 table with each top shape annotated by which question explained it, the change proposed for it, and the p95 expected after; the storage-layer numbers from questions 2 and 5 with their fixes; and a change plan in which every item is staged on one replica, measured, and reversible. The measurement discipline is what separates a review from an opinion: a recommendation that cannot name the metric that will move, and by roughly how much, does not go in the report.

Two mistakes recur across engagements. The first is starting at question 10 because a setting is the fastest thing to change, and finding three weeks later that the sort key was wrong all along. The second is changing several things at once so that the improvement cannot be attributed and the regression cannot be isolated. The performance mistakes and performance pitfalls posts list the rest.

Reading the ClickHouse performance numbers as an SLO

The review’s thresholds are inputs to a service objective, not the objective itself. What the business experiences is a latency percentile for a named set of query shapes (the dashboard shapes, the API shapes, the batch shapes) and an ingestion lag for each pipeline, and the ClickHouse performance review is the mechanism that keeps those within their targets.

Each shape group gets an SLI (p95 or p99 from system.query_log, filtered by the user or the comment tag that identifies the group), a target, and an error budget, and the ten questions are re-run whenever a budget is burning faster than planned.

Recording the ten thresholds beside the SLOs turns a one-time audit into a standing control: the same query that produced the review table runs weekly, and a row crossing its threshold is a ticket rather than a surprise.

Illustrative targets from engagements, to be replaced by the customer’s own: dashboard shapes p95 under 500 ms with a 99.5 percent monthly budget, API shapes p99 under 2 s, batch shapes finishing inside their window nine runs in ten, and ingestion lag under 60 s for streaming pipelines. The reliability hub covers the SLO framework in full.

Version notes

EXPLAIN indexes = 1 is 21.x and later. Projections are stable since 22.x. async_insert is stable since 22.x. Workload scheduling (SETTINGS workload) is 23.x and later. ProfileEvents as a map column in system.query_log is 21.x and later; earlier versions use paired array columns. The ConcurrentQueries profile event in the question-8 query is illustrative; the real series is sampled from system.metrics. The ClickHouse query_log documentation is the reference for every column used above; confirm on the running version.

Reading the archive

The archive is large enough that reading it in question order is the practical route: the audit and settings posts frame the whole review, the storage-layer posts answer questions 2 to 5, and the profiler, memory, concurrency and sharding posts answer 6 to 9.

The 26.8 LTS audit tips post is the current worked example of this review. Then follow the question that matches the symptom: ingestion and merge posts for 2 and 5, the pruning and MergeTree posts for 3, IOPS for 4, the profiler and eBPF posts for 6, thread contention and mixed workloads for 8, sharding for 9, and the settings post for 10.

ChistaDATA runs this review as the opening phase of every ClickHouse consulting engagement and repeats it quarterly under 24×7 support. Every change it proposes is tested on staging with a production-sized sample, applied to one replica first, and shipped with a rollback; a tested restore exists before any sort-key rebuild begins.

ClickHouse performance audit order on 26.8 LTS: rank the workload in system.query_log, measure cold, EXPLAIN ANALYZE, pruning and indexes, storage shape, background work, pipeline stalls, then settings
ChistaDATA Performance

ClickHouse Performance Audit: 8 Best Tips for 26.8 LTS

ChistaDATA Inc.
ClickHouse performance audit tips for 26.8 LTS with real lab output: rank query shapes in system.query_log, measure cold past the query condition cache, read EXPLAIN ANALYZE and EXPLAIN WHATIF, and audit codecs per block.

[…]

Real-Time Payments Analytics
ClickHouse

Real-Time Payments Analytics on ClickHouse: 6 Proven Layers for Southeast Asia

ChistaDATA Inc.
A reference model for real-time payments analytics on ClickHouse with the Apache stack (Kafka, Flink, Iceberg, Spark, Airflow, Superset) for a high-volume Southeast Asian mobile payment platform: schema, ingestion, serving, residency and operations.

[…]

ClickHouse Performance Settings
ClickHouse

ClickHouse Performance Settings in 26.8 LTS: 14 Proven Changes

ChistaDATA Inc.
The ClickHouse performance settings that changed between 26.3 LTS and 26.8 LTS: what each default does to ingest, aggregation, joins and distributed execution, how to measure it, and the rollout method we use.

[…]

Real-time analytics on ClickHouse
ChistaDATA

Real-Time Analytics on ClickHouse 26.8: 6 Proven Levers

ChistaDATA Inc.
Real-time analytics on ClickHouse 26.8: the ingest-to-dashboard latency budget, async-insert freshness, top-K and cache serving, lightweight UPDATE, and ingest/serving isolation on self-managed clusters versus ClickHouse Cloud.

[…]

ClickHouse 26.8 LTS
ChistaDATA

ClickHouse 26.8 LTS: 7 Essential Performance and HA Changes

ChistaDATA Inc.
ClickHouse 26.8 LTS read for production: adaptive aggregation, IEJoin, plan-based parallel replicas, Keeper on disk, always_fetch_mutated_part, and the 26.3 to 26.8 breaking-change checklist for self-managed and Cloud.

[…]

ClickHouse 26.8
ClickHouse

ClickHouse 26.8 LTS: What’s New for Real-Time Analytics (Tested)

ChistaDATA Inc.
ClickHouse 26.8 shipped on 27 August 2026 as the second LTS line of the year, and two days later the 25.8 LTS line dropped out of support. If you run 25.8 in production, you are […]
ClickHouse Partition Pruning
ClickHouse

ClickHouse Partition Pruning vs Primary Key: 3 Proven Gates

ChistaDATA Inc.
ClickHouse partition pruning and primary-key pruning are two different mechanisms that happen to show up on the same summary line of the server log, which is why so many people treat them as one thing. […]
ClickHouse Sharding
ClickHouse

ClickHouse Sharding Troubleshooting and Performance Optimization

ChistaDATA Inc.
A field guide to ClickHouse sharding troubleshooting: Distributed send-queue backlogs, duplicate rows from internal_replication, shard skew, initiator merge bottlenecks, distributed JOIN failures and unavailable shards, each with the system.* evidence, the mechanism, the staged fix, and the alert that catches it early.

[…]

ClickHouse Performance

ClickHouse Performance Troubleshooting: 9 Proven Techniques for 26.7

ChistaDATA Inc.
ClickHouse performance troubleshooting is mostly a reading problem, not a tooling problem. A 26.7 server already writes down almost everything you need — every query it ran, every byte it touched, every microsecond each operator […]
ClickHouse Ingestion Performance
ClickHouse

ClickHouse Ingestion Performance: Batch Sizing, Async Inserts, and Kafka Pipelines

ChistaDATA Inc.
ClickHouse ingestion performance is decided long before the query planner ever touches your data. It is decided by how many parts your writers create per second, how much background merge work those parts generate, and […]

Posts pagination

1 2 … 34 »

ChistaDATA is committed to open source software and building high performance ColumnStores

In the spirit of freedom, independence and innovation. ChistaDATA Corporation is not affiliated with ClickHouse Corporation 

Tell us how we can help!

Loading

Search ChistaDATA Website

★READ THIS WARNING★

* Everything changes over time – Our blogs/posts and comments changes over time, That’s how it should be! Whatever we comment from ChistaDATA Inc. Teams (including Shiv Iyer) and other stakeholders or guest bloggers posted here are never permanent, These things worked for us. But, there is no guarantee they will work for you too, When using the recommendations from ChistaDATA or MinervaDB or MinervaSQL or any other online resources / Google,  You must test the advice before applying them to your production systems, and always invest for a robust Database DR solution, Thank you for understanding. 

Recent Posts from ChistaDATA

  • Real-Time Analytics ClickHouse Workshop for CTOs and Data Architects
  • ClickHouse Performance Audit: 8 Best Tips for 26.8 LTS
  • Real-Time Payments Analytics on ClickHouse: 6 Proven Layers for Southeast Asia
  • ClickHouse Performance Settings in 26.8 LTS: 14 Proven Changes
  • ClickHouse Troubleshooting Techniques: 8 Proven Drills We Teach

☎ TOLL FREE PHONE (24*7)

(844)395-5717

🚩 ChistaDATA Inc. FAX

+1 (209) 314-2364

CORPORATE ADDRESS: CALIFORNIA

ChistaDATA Inc.
440 N BARRANCA AVE #9718 COVINA,
CA 91723
════════════════════════════════
Email: info@chistadata.com

CORPORATE ADDRESS: NEW CASTLE, DELAWARE

ChistaDATA Inc.,
256 Chapman Road STE 105-4,
Newark, New Castle 19702,
Delaware
════════════════════════════════
Email: info@chistadata.com

CORPORATE ADDRESS: DELAWARE

ChistaDATA Inc.,
PO Box 2093 PHILADELPHIA PIKE #3339
CLAYMONT, DE 19703
════════════════════════════════
Email: info@chistadata.com

HOW CAN WE HELP?

We are committed to building Optimal, Scalable, Highly Available, Reliable, Fault-Tolerant and Secured Database Infrastructure Operations for WebScale to our customers globally

CHISTADATA IS COMMITTED TO OPEN SOURCE SOFTWARE AND BUILDING HIGH PERFORMANCE COLUMNSTORES

In the spirit of freedom, independence and innovation. ChistaDATA Corporation is not affiliated with ClickHouse Corporation 

ChistaDATA Inc. Knowledge base is licensed under the Apache License, Version 2.0 (the “License”)

Copyright 2022 ChistaDATA Inc

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

PostgreSQL is a registered trademark of the PostgreSQL Community Association. ClickHouse is a registered trademark of ClickHouse, Inc. MongoDB is a registered trademark of MongoDB, Inc. Couchbase is a registered trademark of Couchbase, Inc. Redis is a registered trademark of Redis Ltd. Apache Cassandra is a registered trademark of the Apache Software Foundation. Milvus is a registered trademark of Zilliz. MinIO is a registered trademark of MinIO, Inc. Amazon Redshift and Amazon Aurora are registered trademarks of [Amazon.com](http://amazon.com/), Inc. Google Cloud is a registered trademark of Google LLC. Snowflake is a registered trademark of Snowflake Inc. Databricks is a registered trademark of Databricks, Inc. MySQL and InnoDB are registered trademarks of Oracle Corporation. MariaDB is a trademark of MariaDB Corporation Ab. All other trademarks are the property of their respective owners. Any other product or company names mentioned may be trademarks or trade names of their respective owners. Copyright © 2010–2026. All Rights Reserved by ChistaDATA®.

Contents

×
  • Question 1: what is the ClickHouse performance workload, in the words of system.query_log?
  • Question 2: are inserts shaped for the part model?
  • Question 3: does the sort key match the predicates? Most ClickHouse performance lives here
  • Question 4: how much of the read is I/O, and from where?
  • Question 5: are parts and merges healthy?
  • Question 6: where does the CPU go?
  • Question 7: is memory the real limit?
  • Question 8: what does concurrency do to ClickHouse performance at the p99?
  • Question 9: is the cluster topology helping or hurting?
  • Question 10: ClickHouse performance settings, and only now
  • What a ClickHouse performance review delivers
  • Reading the ClickHouse performance numbers as an SLO
  • Version notes
  • Reading the archive
→ Index