目錄
1. 主鍵索引 (Primary Key Index) - 核心是稀疏索引
2. 跳數索引 (Data Skipping Indexes) - 二級索引
3. 關鍵總結與最佳實踐:
ClickHouse的索引設計哲學與其他傳統OLTP數據庫(如MySQL)有顯著不同,它更側重于高效掃描大數據集和快速過濾,而不是點查(Point Lookup)。其核心索引類型如下:
1. 主鍵索引 (Primary Key Index) - 核心是稀疏索引
-
- 原理: 這是ClickHouse最核心的索引機制。它不是傳統意義上的B樹索引。
- 數據結構: 本質上是一個稀疏索引。數據在物理存儲上嚴格地按照主鍵(或ORDER BY鍵,兩者在MergeTree引擎家族中概念等價)排序存儲。
- 粒度: 索引并不是為每一行都建立條目,而是為每個數據部分的每個索引粒度(默認為8192行)記錄該粒度內第一行的主鍵值(稱為“標記”)。
- 工作方式:
-
-
- 當執行帶有
WHERE
條件的查詢(特別是涉及主鍵前綴或范圍條件)時,ClickHouse利用這些稀疏的標記值。 - 它通過二分查找快速定位到可能包含目標數據的數據塊(即索引粒度對應的數據段)。
- 然后,它只加載這些定位到的數據塊(
.bin
文件中的相應部分)到內存中進行掃描和過濾,避免了全表掃描。
- 當執行帶有
-
-
- 優勢:
-
-
- 內存占用小: 稀疏索引占用的內存遠小于稠密索引(如B樹)。
- 范圍查詢極快: 對于按主鍵排序的范圍查詢(如時間范圍),效率極高,只需加載少量相關數據塊。
- 高效過濾: 能有效利用主鍵前綴進行過濾。
-
-
- 局限性:
-
-
- 點查效率較低: 查找單行數據時,可能仍需加載一個完整的數據塊(8192行)進行掃描,不如B樹高效。ClickHouse不是為高頻點查設計的。
- 依賴主鍵順序: 查詢條件必須有效利用主鍵(或ORDER BY鍵)的前綴才能發揮最大效果。如果查詢條件不涉及主鍵前綴,索引效果會大打折扣甚至無效。
- 非唯一約束: 聲明主鍵不強制唯一性約束,它只定義數據的物理排序順序和稀疏索引的構建依據。
-
-
- 存儲位置: 索引標記存儲在內存中(
primary.idx
文件在磁盤上,啟動時加載到內存),查詢時進行二分查找速度很快。
- 存儲位置: 索引標記存儲在內存中(
2. 跳數索引 (Data Skipping Indexes) - 二級索引
-
- 目的: 為了解決主鍵索引對非主鍵列(特別是高基數列或查詢條件不涉及主鍵前綴時)過濾效率低下的問題。
- 原理: 在數據塊(索引粒度)級別上存儲關于該塊內數據的摘要信息(Min/Max, Bloom Filter, 集合等)。查詢時,先檢查這些摘要信息,如果確定某個數據塊內不可能包含滿足查詢條件的數據,則跳過整個數據塊的讀取。
- 核心思想: 跳過不需要掃描的數據塊,減少IO和CPU消耗。
- 類型: ClickHouse提供了多種跳數索引類型,適用于不同的數據類型和查詢模式:
-
-
minmax
: 存儲每個數據塊中指定列的最小值和最大值。適用于數值、日期等有序類型。如果查詢條件范圍與某個塊的min-max范圍無重疊,則跳過該塊。最簡單高效,通常首選。set
: 存儲每個數據塊中指定列的所有唯一值(或一個固定大小的超集)。適用于低中基數列的IN
或=
查詢。如果查詢值不在塊的set中,則跳過該塊。存儲開銷和構建成本隨基數增大而增加。bloom_filter
: 為每個數據塊中的指定列值構建一個布隆過濾器。適用于高基數列的IN
或=
查詢,尤其是字符串。可以高效判斷某個值“可能不存在”于該塊(有一定誤判率,但“存在”判斷是準確的)。需要配置參數(false_positive
概率)。ngrambf_v1
/tokenbf_v1
: 基于N-gram或分詞構建的布隆過濾器變種,專門優化字符串子串(LIKE
,%term%
) 和分詞搜索的跳過。experimental
類型: 如hypothesis
索引(基于統計假設檢驗)等,用于特定高級場景。
-
-
- 創建: 在
CREATE TABLE
或ALTER TABLE
語句中聲明。例如:
- 創建: 在
CREATE TABLE logs (timestamp DateTime,user_id UInt64,url String,...INDEX idx_user_id user_id TYPE set(100) GRANULARITY 4,INDEX idx_url_token url TYPE tokenbf_v1(32768, 3, 0) GRANULARITY 4
) ENGINE = MergeTree
ORDER BY (timestamp, user_id);
-
-
GRANULARITY
: 指定索引的粒度(覆蓋多少個主鍵索引粒度)。GRANULARITY 1
表示每個跳數索引條目對應一個主鍵索引粒度(8192行)。GRANULARITY 4
表示一個跳數索引條目對應4個主鍵索引粒度(32768行)。更大的粒度可以減少索引大小但降低跳過精度。
-
-
- 優勢:
-
-
- 顯著加速對非主鍵列(尤其是高基數列)的過濾查詢。
- 減少不必要的磁盤IO和CPU消耗。
-
-
- 局限性:
-
-
- 額外存儲和計算開銷: 需要存儲索引數據,并在寫入和合并時計算摘要信息。
- 可能無效: 如果數據分布使得摘要信息無法有效跳過塊(例如所有塊的值范圍都很大且重疊),則索引效果差。
- 布隆過濾器誤判:
bloom_filter
類型可能導致不必要的塊讀取(但不會漏讀)。 - 粒度影響:
GRANULARITY
設置需要權衡索引大小和跳過精度。
-
3. 關鍵總結與最佳實踐:
- 主鍵索引是基石: 表設計時,
ORDER BY
子句(即主鍵)的選擇至關重要。優先選擇最常用作過濾條件(尤其是范圍過濾)的1-N個列。良好的主鍵設計能解決大部分高效查詢問題。 - 跳數索引是補充: 當查詢條件無法有效利用主鍵前綴或涉及高基數的非主鍵列時,考慮創建合適的跳數索引。
- 索引選擇:
-
- 優先嘗試
minmax
(對有序類型)。 - 對低基數的
IN
/=
用set
。 - 對高基數的
IN
/=
或字符串用bloom_filter
。 - 對
LIKE
/子串搜索用ngrambf_v1
/tokenbf_v1
。
- 優先嘗試
- 評估與監控: 使用
EXPLAIN indexes = 1
查看查詢是否使用了索引以及跳過了多少數據塊。監控索引的存儲開銷和查詢性能提升是否成正比。 - 理解ClickHouse哲學: ClickHouse索引的目標是最小化需要從磁盤讀取的數據量,通過預排序(主鍵索引) 和 數據塊摘要(跳數索引) 來實現。它不是為單行檢索優化,而是為海量數據分析的聚合和掃描優化。
總而言之,ClickHouse的索引機制是其高性能查詢的核心支撐之一。深刻理解主鍵(稀疏索引)和跳數索引的原理、適用場景以及局限性,是設計和優化ClickHouse表結構、編寫高效查詢的關鍵。