首页/数据/NoSQL 速查/跳过索引(skip index)设计
ClickHouse56 条

跳过索引(skip index)设计

跳过索引不定位具体行,只帮引擎跳过大块 granule:常用 minmax、set、bloom_filter、ngrambf_v1/tokenbf_v1;列数据越有序,收益越明显。

NoSQL 速查数据更新于 2026-10-13

跳过索引的原理

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 很少,应直接删掉。

易错点

  • 只在新增数据上生效却以为历史数据也覆盖了:必须执行 MATERIALIZE INDEX,否则老数据依旧全扫
  • 索引表达式与查询写法不一致:例如索引建在字段上、查询里却对字段做函数包裹,两侧不匹配就无法命中
  • GRANULARITY 调得过大:索引变小但跳过粒度变粗,实际收益不升反降
  • 在低基数列上建 bloom_filter:本该用 set(N) 精确跳过,bloom 反而带来误判与多余开销

数据来源与参考

  • ClickHouse Docs: Data skipping indexes
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌