在資料量日益龐大的場景下,如何優化查詢效能 是每一位開發者必須具備的核心能力。本篇將帶你實戰演練 ClickHouse 中兩個查詢優化利器:
system.query_log 是 ClickHouse 內建的查詢歷史紀錄表,它會紀錄每一筆查詢的:
這邊只紀錄「查詢執行的資訊」,並不會紀錄查詢的結果資料。
SELECT
query_start_time,
query_duration_ms,
read_rows,
result_rows,
query
FROM system.query_log
WHERE event_time > now() - INTERVAL 10 MINUTE
AND type = 'QueryFinish'
AND query LIKE 'SELECT%'
ORDER BY query_start_time DESC
LIMIT 100;
欄位 | 說明 |
---|---|
query_start_time | 查詢開始時間 |
query_duration_ms | 查詢耗時 (毫秒) |
read_rows | 查詢過程中讀取的 row 數量 |
result_rows | 查詢結果輸出的 row 數量 |
memory_usage | 查詢執行時的記憶體使用量 (Byte) |
type 值 | 說明 |
---|---|
QueryStart = 1 |
查詢開始執行時紀錄 |
QueryFinish = 2 |
查詢成功完成時紀錄 |
ExceptionBeforeStart = 3 |
查詢還沒執行就錯誤時紀錄 |
ExceptionWhileProcessing =4 |
查詢執行中發生錯誤時紀錄 |
SELECT
query_start_time,
query_duration_ms,
read_rows,
memory_usage,
query
FROM system.query_log
WHERE event_time > now() - INTERVAL 1 HOUR
AND type = 'QueryFinish'
AND query_duration_ms > 500 -- 大於 500ms
ORDER BY query_duration_ms DESC;
ClickHouse 提供 EXPLAIN 語法,讓你在查詢前預測 查詢路徑、掃描資料量、JOIN 策略 等細節。
EXPLAIN [AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE] [settings]
SELECT ...
模式 | 說明 |
---|---|
AST | 顯示查詢的抽象語法樹 (Abstract Syntax Tree)。 |
SYNTAX | 顯示經過語法優化後的查詢結構。 |
QUERY TREE | 顯示查詢邏輯樹,反映優化器進行後的結構。 |
PLAN | 查詢的執行計畫路徑(含掃描表、JOIN 策略等)。 |
PIPELINE | 查詢的執行階段與並行度資訊 (執行緒、流水線處理器等)。 |
ESTIMATE | 預估查詢將掃描的資料量(rows、marks、parts)。 |
TABLE OVERRIDE | 驗證 table function 的 schema 覆寫是否正確。 |
EXPLAIN SYNTAX SELECT * FROM system.numbers WHERE number < 10;
SELECT *
FROM system.numbers
WHERE number < 10
EXPLAIN QUERY TREE SELECT id, value FROM test_table;
QUERY id: 0
PROJECTION COLUMNS
id UInt64
value String
JOIN TREE
TABLE id: 3, table_name: default.test_table
這能讓你清楚知道查詢會如何去 Join Tables、哪些欄位會被投影出來。
EXPLAIN PLAN SELECT sum(number) FROM numbers(1000) GROUP BY number % 4;
Union
Expression (Projection)
Aggregating
ReadFromStorage (SystemNumbers)
你可以看到從讀取資料到聚合的整個查詢執行路徑。
EXPLAIN ESTIMATE SELECT * FROM large_table WHERE date >= '2024-01-01';
┌─database─┬─table──────┬─parts─┬─rows───┬─marks─┐
│ default │ large_table│ 2 │ 500000 │ 32 │
└──────────┴────────────┴───────┴────────┴───────┘
SELECT
query_start_time,
query_duration_ms,
read_rows,
read_bytes,
memory_usage,
query
FROM system.query_log
WHERE event_time > now() - INTERVAL 1 HOUR
AND type = 'QueryFinish'
AND query LIKE '%order_summary%'
ORDER BY query_duration_ms DESC
LIMIT 5;
query_duration_ms: 4500ms
read_rows: 100000000
query: SELECT region, SUM(amount) FROM order_summary GROUP BY region;
EXPLAIN PLAN SELECT region, SUM(amount) FROM order_summary GROUP BY region;
Expression (Projection)
Aggregating
ReadFromMergeTree (order_summary)
全表掃描!
- 查詢條件沒有加上 Partition Key (date)。
- order_summary 按 (date, region) 分區,但查詢沒帶 date 範圍 → 全表掃描。
- 可考慮將 region 聚合寫入 Materialized View 預先計算。
SELECT region, SUM(amount)
FROM order_summary
WHERE date = today() - 1
GROUP BY region;
SELECT query_duration_ms FROM system.query_log
WHERE query LIKE '%order_summary%'
AND event_time > now() - INTERVAL 5 MINUTE
AND type = 'QueryFinish'
ORDER BY query_start_time DESC
LIMIT 1;
query_duration_ms: 300ms
4500ms
->300ms
(Nice Try Diddy)
SELECT user_id, COUNT(*) FROM user_events GROUP BY user_id;
EXPLAIN PLAN SELECT user_id, COUNT(*) FROM user_events GROUP BY user_id;
Expression (Projection)
Aggregating
ReadFromMergeTree (user_events)
完全沒有 Index 篩選,直接全表掃描。
user_events 的 Partition Key 是 EventDate,所以我們加上日期範圍:
SELECT user_id, COUNT(*)
FROM user_events
WHERE EventDate >= today() - 7
GROUP BY user_id;
EXPLAIN ESTIMATE
SELECT user_id, COUNT(*)
FROM user_events
WHERE EventDate >= today() - 7;
┌─database─┬─table────────┬─parts─┬─rows──────┬─marks─┐
│ default │ user_events │ 3 │ 10000000 │ 800 │
└──────────┴──────────────┴───────┴───────────┴───────┘
原本未加條件時掃描了 1 億筆 rows,現在僅掃描 1 千萬筆,資料量明顯下降。
EXPLAIN PIPELINE
SELECT user_id, COUNT(*)
FROM user_events
WHERE EventDate >= today() - 7
GROUP BY user_id;
確認查詢能夠使用多個 AggregatingTransform 節點平行處理。
SELECT query_duration_ms FROM system.query_log
WHERE query LIKE '%user_events%'
AND event_time > now() - INTERVAL 5 MINUTE
AND type = 'QueryFinish'
ORDER BY query_start_time DESC
LIMIT 1;
query_duration_ms: 600ms
EXPLAIN 是 ClickHouse 優化查詢性能的核心工具,透過不同模式,你可以: