system.query_log to find recurring slow-query patterns, choose a representative run, and review its resource usage. You will then use EXPLAIN to inspect the query plan and form a hypothesis about the bottleneck before changing the query or schema.
Before you begin
The examples in this guide use thenyc_taxi.trips_small_inferred table. To run them as written, create and load the table if you have not already done so:
Set up the example dataset
Set up the example dataset
The source Parquet file is approximately 5.8 GB. Loading it can take several minutes, depending on your network and available resources.
SYSTEM FLUSH LOGS, wait for the query log to flush automatically, then retry the first lookup. When diagnosing your own workload, ensure that system.query_log contains completed runs from the time range you intend to inspect.
How it works
By default, ClickHouse records information about completed queries in thesystem.query_log table. Each record can include the query duration, the number of rows read, CPU and memory usage, and filesystem cache activity.
These measurements help you identify slow query patterns and understand how they use resources. After choosing a representative run, you can inspect its query plan to investigate where the query might be spending time.
On a cluster, query-log data remains local to each node. The examples in this guide use clusterAllReplicas to query every replica and merge to include the current system.query_log table and any versioned query_log_N tables retained after system-table schema changes.
Each query-log example includes tabs for clustered and single-node deployments. ClickHouse Cloud provides the default cluster used in the cluster examples. In a self-managed deployment, replace default with a cluster listed in system.clusters.
The examples set
skip_unavailable_shards so that a temporarily unavailable replica does not cause the diagnostic query to fail. This is particularly useful during autoscaling. Records from a skipped replica are not included, so the results may be incomplete.Diagnose a slow query
With completed runs in the query log, work through these three steps in order. You will identify a recurring slow-query pattern, choose a representative run, and inspect the query’s execution plan.Identify candidate queries
Start by grouping completed initial queries by Use The
normalized_query_hash. This separates query patterns that recur from individual slow executions. The following query ranks patterns by their median duration and includes an example query for each pattern:- Cluster
- Single node
executions to distinguish recurring workload from isolated queries. A pattern with a high median duration, frequent executions, or high resource usage is a stronger candidate for investigation than a single slow run.As a quick inventory, the following query lists the slowest completed run for up to five distinct query patterns on the NYC Taxi dataset. It excludes dataset-loading statements and repeated runs of the same pattern. In the next step, you will narrow the query history to runs with the normalized_query_hash you selected above.- Cluster
- Single node
query_duration_ms field contains the query duration in milliseconds. In these results, the longest-running query took 2,967 ms.You can also identify candidate queries based on resource usage rather than query duration:Find resource-intensive queries
Find resource-intensive queries
This query ranks recent queries by memory usage and includes their CPU usage. Results vary by workload and deployment:
- Cluster
- Single node
Choose a representative query run
A single slow run might be an outlier caused by an ad hoc query or temporary system load. Before inspecting the query plan, review several completed runs with the same The example query-log results show that each candidate read approximately 329.04 million rows. For context, confirm the number of rows in the example table:The table contains 329.04 million rows, approximately the same number reported in
normalized_query_hash, which is identical for queries that differ only by literal values. Choose a run that represents the pattern’s typical duration and resource usage.Replace the value assigned to selected_hash with the normalized_query_hash of the pattern you want to investigate:- Cluster
- Single node
- Find runs with similar
read_rowsandread_bytes. - Compare
query_duration_msandmemory_usagefor those runs. - Select the
query_idwhosequery_duration_msis closest to the median.
Historical query-log results can vary with cache state and system load, so use them to choose a query to investigate, not to compare optimization changes. If the query log does not contain enough completed runs, run the query a few times under similar conditions. The next guide, Isolate query bottlenecks, explains how to collect controlled measurements for comparing changes.
read_rows for each candidate. This suggests that the queries scanned most or all of the table, but it does not identify why those rows were read or whether that amount is appropriate for the query. Inspect the query plan next to see how ClickHouse selected and processed the data.Inspect the execution plan
After choosing a representative run, use The output includes the following operations. Details such as the number of parts and granules depend on how the data is stored:From the bottom up, the plan maps to the query as follows:
EXPLAIN to inspect how ClickHouse plans the query without running it. The output shows the operations ClickHouse expects to perform and how data moves between them, providing more context for the measurements in the query log.For a detailed introduction to the available output formats, see Understanding query execution with the analyzer. In this example, EXPLAIN shows how ClickHouse plans to read and filter the data and whether it can skip any of it.The output is a tree of operations that shows how ClickHouse expects to read, filter, and process the data. Child operations appear below their parents. Start with the deepest read operation, then follow the plan upward to see how ClickHouse transforms the data into the final result.For this example, inspect the calculated-speed query from the query-log results:ReadFromMergeTreereads fromnyc_taxi.trips_small_inferred. The missingIndexessection, combined withread_rowsmatching the table’s row count, shows that ClickHouse reads the entire table.Filtershows the expanded expression forspeed_mph > 30. For every row read, ClickHouse calculates the trip duration and speed, then keeps only rows above 30 miles per hour.Aggregatingcalculates the quantiles from the filteredtrip_distancevalues.
speed_mph while filtering, and calculating the quantiles.