ClickHouse跳过索引需WHERE条件匹配索引表达式且类型适配,仅对新数据生效,须用EXPLAIN indexes=1和system.query_log验证是否真正跳块。

跳过索引不是“加了就快”,得看 WHERE 条件能不能对上
ClickHouse 的 skip index 不是传统 B-tree 索引,它不加速单行定位,而是帮引擎跳过整块(granule)数据。所以它生效的前提很具体:查询的 WHERE 条件必须能被索引表达式“覆盖”,且索引类型要匹配过滤逻辑。
- 比如建了
INDEX idx_user_id user_id TYPE set(100) GRANULARITY 2,那WHERE user_id IN (1,2,3)可能跳块;但WHERE toString(user_id) LIKE '1%'就完全用不上——表达式变了,索引失效 -
minmax对BETWEEN、>=、有效,但对!=或NOT IN基本无用 - 索引只作用于新写入的数据;已有分区需显式执行
MATERIALIZE INDEX才能生效
选对 TYPE 是关键,别乱套模板
不同 TYPE 解决不同问题,硬套会白费存储还拖慢写入:
-
minmax:适合时间戳、ID 等单调或近似单调字段,范围查询快;但若列值在 granule 内剧烈跳变(比如乱序插入的user_id),min/max 范围太宽,跳块率骤降 -
set(N):适合枚举类、低基数字段(如status、country_code);N是每个 granule 最多存多少个去重值,超了整个 granule 就不索引——设太小没用,太大占空间 -
ngrambf_v1(n, size, hash, seed):文本模糊查必备,比如WHERE page_url LIKE '%/api/v2/%';但注意它不支持正则,只对 n-gram 子串做布隆过滤,误判率存在,且size过小会导致漏跳
GRANULARITY 不是越大越好,得和主索引粒度对齐
ClickHouse 默认主索引粒度是 8192 行(index_granularity),而 skip index 的 GRANULARITY 指“几个粒度组成一个索引块”。设成 4,就是每 32768 行建一个索引项。
- 设太小(如
GRANULARITY 1):索引文件暴涨,写入时 CPU 和磁盘压力明显上升,尤其高并发写入场景 - 设太大(如
GRANULARITY 16):每个索引块覆盖行数过多,过滤精度下降,可能本该跳过的块没跳过去 - 经验建议:从
GRANULARITY 2或4起步;观察system.parts中marks和data_compressed_bytes变化,再微调
怎么确认 skip index 真正在干活?别只看执行时间
加速效果不能光比查询耗时,得看底层是否真跳了块。最直接的方式是查 EXPLAIN indexes = 1:
EXPLAIN indexes = 1 SELECT * FROM events WHERE site_id = 123;
输出里如果出现 Using primary key index + Using skipping index idx_site_id,说明双索引协同生效;若只有前者,说明 skip index 没触发。
- 再进一步,查
system.query_log,过滤出该查询的read_rows和read_bytes,对比加索引前后——跳块成功时,这两项应显著下降 - 常见陷阱:WHERE 中用了函数包裹列(如
toYear(event_date)),但索引建在event_date上,此时索引失效;必须建表达式索引:INDEX idx_year toYear(event_date) TYPE minmax GRANULARITY 2
skip index 是把双刃剑:写入变重、存储略增、配置稍复杂,但它对高频过滤场景(尤其是高基数列 or 文本模糊查)的收益非常实在。真正难的不是“怎么建”,而是“建完后怎么验证它有没有在跳”——别让索引躺在表结构里睡大觉。










