跳过索引的原理
ClickHouse 的数据按 granule 分块存储,跳过索引(data skipping index)不为每一行建立定位,只在每个 granule 上记录一段摘要。查询时先看摘要把不可能命中的 granule 整块跳过,从而减少实际读取量。因此它的收益完全取决于两条:列数据是否有序(minmax 才有效),以及条件的选择性是否足够高。
基本信息
| 适用平台 | ClickHouse MergeTree 家族 |
|---|---|
| 定义语法 | ALTER TABLE t ADD INDEX idx expr TYPE <类型> GRANULARITY n |
| 回填已有数据 | ALTER TABLE t MATERIALIZE INDEX idx(新数据自动维护) |
| 验证方式 | EXPLAIN indexes = 1 查看各 granule 的跳过情况 |
| 常用类型 | minmax 范围、set(N) 低基数枚举、bloom_filter、ngrambf_v1 子串、tokenbf_v1 分词 |
| 参数说明 | bloom_filter([false_positive_rate]);ngrambf_v1(n, 字节数, 哈希数, 随机种子);tokenbf_v1(字节数, 哈希数, 随机种子) |
典型使用场景
- 低基数枚举列:状态、类型、错误码,用 set(N) 精确跳过
- 有序数值列的范围查询:时间戳、ID,用 minmax 收益最大
- 字符串等值查询:URL、请求 ID,用 bloom_filter 按概率跳过
- 日志内容检索:msg 字段上建 ngrambf_v1 支持子串匹配
- 分词后的关键词过滤:tokenbf_v1 适合英文单词与部分符号切分
- 大表临时排查:先加索引观察跳过量,验证有效后再保留
定义与验证
定义与验证
-- 低基数列:set(N) 记录 granule 内出现过的取值
ALTER TABLE events ADD INDEX idx_type event_type TYPE set(100) GRANULARITY 4;
-- 子串检索:ngrambf_v1(n 元长度, 过滤位图字节数, 哈希函数个数, 随机种子)
ALTER TABLE events ADD INDEX idx_msg msg TYPE ngrambf_v1(3, 10000, 3, 0) GRANULARITY 4;
-- 新增索引只对之后写入的数据生效,已有数据要显式回填
ALTER TABLE events MATERIALIZE INDEX idx_msg;
-- 验证:EXPLAIN 的 Indexes 段落会给出跳过的 granule 数量
EXPLAIN indexes = 1 SELECT count() FROM events WHERE event_type = 'click';
SELECT count() FROM events WHERE msg LIKE '%timeout%';类型对照
| 类型 | 适合的条件 | 代价与注意 |
|---|---|---|
| minmax | 范围与等值条件,列数据有序时最有效 | 索引极小;数据无序时几乎跳不掉 granule |
| set(N) | 取值个数不超过 N 的枚举列 | N 设得比实际基数小会退化,需按真实基数配置 |
| bloom_filter | 高基数字符串的等值查询 | 按概率误判,存在少量无效读取 |
| ngrambf_v1 | 子串匹配(LIKE '%x%') | 索引体积大,n 与哈希数需实测调优 |
| tokenbf_v1 | 按分词切出的关键词查询 | 中文切分效果有限,长词与数字序列命中率低 |
跳过索引的粒度由 GRANULARITY 决定:值越大索引越小,但一次最多只能跳过一整块 granule,跳过粒度变粗。先用 EXPLAIN 看实际跳过量,再决定是否需要继续加索引。
跳过索引不是万能加速器:数据量小、条件选择性低或列本身无序时,写入与存储成本白付。加索引后若 EXPLAIN 显示跳过的 granule 很少,应直接删掉。