CodeWalk

ClickHouse MergeTree主键索引与稀疏索引机制

作者:我还是少年 · 2026-05-30 12:55

ClickHouse MergeTree的主键索引是稀疏索引(Sparse Index),请解释其工作原理:index_granularity和index_granularity_bytes如何控制索引粒度?主键索引如何加速查询(Granule裁剪机制)?与MySQL B+Tree索引有什么本质区别?给出一个ORDER BY设计的优化案例。

回答

我还是少年

ClickHouse稀疏索引工作原理:

1. 索引结构

  • 不是B+Tree,而是排序后的列值数组
  • index_granularity(默认8192)行记录一个索引条目
  • 索引文件primary.idx存储在列数据之外的元数据层

2. 索引粒度参数

-- 表级设置
ENGINE = MergeTree
ORDER BY (CounterID, EventDate)
SETTINGS index_granularity = 8192, index_granularity_bytes = 10485760;
  • index_granularity:每N行生成一个索引标记(固定行数)
  • index_granularity_bytes:每M字节生成一个索引标记(自适应,10MB)
  • 两者取最先达到的阈值

3. Granule裁剪机制

SELECT * FROM table WHERE CounterID = 12345;
  1. 二分查找primary.idx,定位到包含CounterID=12345的Granule范围
  2. 只读取可能包含目标数据的Granule(跳过无关Granule)
  3. 每个Granule内部顺序扫描(少量数据)

4. 与MySQL B+Tree的本质区别

维度ClickHouse稀疏索引MySQL B+Tree
存储位置与数据分离与数据集成(聚簇索引)
粒度每8192行一个标记每行一个键值
查找方式二分查找GranuleB+Tree多级查找
适用范围扫描/批量聚合点查/小范围查
内存占用极小(1个Granule≈1个int)较大(维护完整树结构)

5. ORDER BY优化案例

-- ❌ 错误:先排低基数,后排高基数
ORDER BY (status, user_id)  -- status=1的值散落在各处

-- ✅ 优化:先排高基数或查询频繁的列
ORDER BY (user_id, event_time)  -- 同一用户数据连续存储