ClickHouse跳数索引的设计与应用:Bloom Filter与MinMax在查询加速中的效果
ClickHouse跳数索引的设计与应用Bloom Filter与MinMax在查询加速中的效果一、全表扫描40亿行ClickHouse也扛不住在一个数据分析场景中表user_events按小时分区存储了40亿条用户行为事件查询找出过去一周所有通过utm_sourcewechat且event_typepurchase的事件耗时18秒。表上已经建了排序键ORDER BY (event_date, user_id)但这个查询中utm_source和event_type都不在排序键前缀中ClickHouse不得不扫描所有粒度的数据块——这就是缺少跳数索引的代价。ClickHouse的稀疏主键索引primary.idx基于排序键只记录每N个粒度默认为8192行的排序键最小值。对于非排序键列的过滤条件主键索引完全用不上。跳数索引就是为了解决这个问题的——在非排序键列上建立轻量级的统计摘要让查询在执行时能够跳过不满足条件的数据块避免不必要的IO和计算。二、跳数索引的过滤原理MinMax、Set与Bloom Filter的代价模型ClickHouse支持多种跳数索引类型核心都是在每个粒度块上存储该列的统计摘要信息。flowchart LR subgraph Granules[数据粒度 (Granules)] G1[Granule 0br/8192 rowsbr/event_type: [click, view, click...]] G2[Granule 1br/8192 rowsbr/event_type: [purchase, purchase...]] G3[Granule 2br/8192 rowsbr/event_type: [view, share, view...]] end subgraph IndexMinMax[MinMax索引] M1[Minclick, Maxview] M2[Minpurchase, Maxpurchase] M3[Minshare, Maxview] end subgraph IndexBloom[Bloom Filter索引] B1[Bloom: {click, view}] B2[Bloom: {purchase}] B3[Bloom: {share, view}] end G1 -- M1 G2 -- M2 G3 -- M3 G1 -- B1 G2 -- B2 G3 -- B3 Q[WHERE event_typepurchase] -.-|MinMax检查| M2 Q -.-|Bloom检查| B2 M2 -.-|匹配| G2[只读取 Granule 2] B2 -.-|匹配| G2 style G2 fill:#c8e6c9 style M2 fill:#bbdefb style B2 fill:#bbdefbMinMax索引存储每个粒度块中列的最小值和最大值。查询时检查WHERE条件是否与MinMax区间有交集如果WHERE price 1000且某个粒度块的Max price是500那么这个粒度块可以直接跳过。MinMax在列值分布与物理存储顺序相关时效果最好——这就是为什么排序键对跳数索引也至关重要。Set索引存储每个粒度块中该列去重后的所有值。对于低基数列最有效——event_type可能只有10个不同的值Set索引可以精确判断某个值是否出现。但如果基数为10万Set索引本身的大小可能超过原始数据得不偿失。Bloom Filter索引使用概率数据结构可以快速判断一个值可能在或一定不在该粒度块中。空间效率远高于Set索引但有假阳性——Bloom Filter返回可能在但实际值不存在时需要实际读取数据块而无用功。假阳性率可以通过调整Bloom Filter的bit数来控制默认设置误差率约1%。三、针对不同查询模式的索引组合策略-- ClickHouse跳数索引创建示例 -- 场景1: 高基数列的等值过滤 → Bloom Filter ALTER TABLE user_events ADD INDEX idx_utm_source_bloom utm_source TYPE bloom_filter(0.01) GRANULARITY 4; -- 场景2: 低基数列的等值过滤 → Set ALTER TABLE user_events ADD INDEX idx_event_type_set event_type TYPE set(100) GRANULARITY 4; -- 场景3: 范围过滤列值与排序键相关 → MinMax ALTER TABLE user_events ADD INDEX idx_amount_minmax amount TYPE minmax GRANULARITY 1; -- 场景4: 时间范围枚举值过滤 → 组合索引 ALTER TABLE user_events ADD INDEX idx_composite (event_type, platform) TYPE bloom_filter(0.01) GRANULARITY 4; -- 场景5: 模糊匹配/前缀匹配 → ngrambf_v1或tokenbf_v1 ALTER TABLE user_events ADD INDEX idx_title_ngram title TYPE ngrambf_v1(3, 512, 2, 0) GRANULARITY 1;跳数索引的性能效果受三个参数影响GRANULARITY控制索引粒度每个索引条目覆盖多少个granuleTYPE决定索引类型和精度索引的物理排序决定了MinMax的有效性。在生产环境中测试过一组真实数据效果如下# 索引效果的简化评估脚本 import time from clickhouse_driver import Client import logging logger logging.getLogger(__name__) class SkipIndexBenchmark: ClickHouse跳数索引效果评估 def __init__(self, client: Client): self.client client def test_query_performance(self, table: str, query: str, with_index: bool True) - dict: 测试查询性能并收集指标 try: # 获取查询统计信息 stats_query f SELECT query, read_rows, read_bytes, query_duration_ms, memory_usage FROM system.query_log WHERE type QueryFinish AND query LIKE %{table}% ORDER BY event_time DESC LIMIT 1 # 先清除缓存 self.client.execute(fSYSTEM DROP MARK CACHE) start time.time() result self.client.execute(query) elapsed time.time() - start return { query: query[:100], with_index: with_index, elapsed_sec: elapsed, rows_returned: len(result), } except Exception as e: logger.error(fBenchmark failed: {e}) return {error: str(e)} def compare_index_effect(self, table: str, column: str): 对比有无跳数索引的查询效果 base_query fSELECT count() FROM {table} WHERE {column} target_value # 无索引测试 self.client.execute(fALTER TABLE {table} DROP INDEX IF EXISTS idx_{column}) no_index self.test_query_performance(table, base_query, with_indexFalse) # 有索引测试 self.client.execute(f ALTER TABLE {table} ADD INDEX idx_{column} {column} TYPE bloom_filter(0.01) GRANULARITY 4 ) self.client.execute(fALTER TABLE {table} MATERIALIZE INDEX idx_{column}) with_index self.test_query_performance(table, base_query, with_indexTrue) return {no_index: no_index, with_index: with_index}四、索引维护成本与查询加速比的非对称博弈跳数索引的维护成本集中在写入路径每次INSERT数据时ClickHouse需要为每个跳数索引更新对应粒度块的统计信息。对于Bloom Filter需要计算新增数据的哈希并更新bit数组对于MinMax需要比较并可能更新边界值。这个开销在批量写入场景下几乎不可见因为一批数据对应少数几个粒度块但在高频小批次写入场景下可能成为瓶颈。GRANULARITY参数是成本和收益的调节旋钮。GRANULARITY 1表示每个粒度块8192行建一个索引条目查得最细但索引体积最大GRANULARITY 4表示每4个粒度块约32K行建一个索引索引体积缩小4倍但可能多读一些不需要的数据。实践中GRANULARITY 4是较好的默认起点。索引物化的时机必须谨慎。MATERIALIZE INDEX会全表扫描构建索引在大于1TB的表上可能运行数小时期间的IO压力会影响在线查询。建议在业务低峰期执行或使用ALTER TABLE ... UPDATE ... WHERE分批构建。五、总结ClickHouse的跳数索引是在排序键索引之外的第二层过滤通过MinMax、Set和Bloom Filter等轻量级统计摘要在查询执行前过滤掉大量不相关的数据块。Bloom Filter是高基数列的最佳选择Set索引适合低基数列MinMax在有序列的范围过滤上效果显著。实践中建议为一个表创建2-4个跳数索引覆盖最高频的过滤条件。记住一个核心原则跳数索引的效果取决于数据在物理存储上的局部性——好的排序键设计能让跳数索引事半功倍。

相关新闻

DMA控制器原理与实战:嵌入式系统数据搬运优化指南

DMA控制器原理与实战:嵌入式系统数据搬运优化指南

1. DMA控制器:嵌入式系统的“数据搬运工”在嵌入式系统开发中,尤其是涉及雷达信号处理、高速数据采集或实时图像处理的场景,我们常常会遇到一个核心矛盾:CPU的计算能力是宝贵的,但大量、频繁的数据搬运工作&#xff08…

2026/10/5 8:37:58 阅读更多 →
杭州积分落户加分攻略:学历提升如何为你的城市生活加分

杭州积分落户加分攻略:学历提升如何为你的城市生活加分

一、杭州积分落户政策:新市民的安居之路作为长三角地区最具吸引力的城市之一,杭州以其优美的自然环境、蓬勃的经济活力和开放包容的城市氛围,每年吸引着大量外来人口前来就业和定居。为了更好地服务外来常住人口,杭州实行了积分落…

2026/9/26 1:46:54 阅读更多 →
搜索场景的大模型推理优化:多阶段召回的精排融合与 Query 理解延迟压缩

搜索场景的大模型推理优化:多阶段召回的精排融合与 Query 理解延迟压缩

搜索场景的大模型推理优化:多阶段召回的精排融合与 Query 理解延迟压缩 一、搜索的全链路延迟分解:从 Query 输入到结果返回的三级跳 搜索系统的一个完整请求链路包含三个阶段:Query 理解(意图分类、实体识别、纠错、改写&#xf…

2026/10/2 7:02:07 阅读更多 →

最新新闻

Simulink S-function内存与实时性底层原理详解

Simulink S-function内存与实时性底层原理详解

1. 为什么S-function不是“写个C函数就完事”——从一个被反复踩爆的初始化陷阱说起 我第一次在汽车电控项目里用S-function,是为了解决Simulink自带的CAN模块无法解析某款国产ECU自定义报文格式的问题。当时信心满满:不就是写个C函数,把 md…

2026/10/5 8:38:16 阅读更多 →
国内开源MES框架选型与落地实践:从原理到部署全解析

国内开源MES框架选型与落地实践:从原理到部署全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 8:38:16 阅读更多 →
S7-1200与HMI时间同步实战:从TIA Portal配置到现场排查

S7-1200与HMI时间同步实战:从TIA Portal配置到现场排查

上个月去一个客户现场处理故障,MES追溯系统显示一批产品的工艺参数全部正常,但批次时间对不上号,整批记录差点被判定为无效。查了大半天,最后发现根源不在工艺、不在网络,而是PLC 1200和HMI的时间差了几个小时。这种问…

2026/10/5 8:38:16 阅读更多 →
Stata因子分析实操指南:从PCA降维到FA构念解构

Stata因子分析实操指南:从PCA降维到FA构念解构

1. 这不是统计课PPT,而是一份能直接跑通的Stata因子分析实操手记你打开Stata,输入pca命令,回车后屏幕上刷出一长串特征值、方差贡献率、成分矩阵——然后呢?接下来该做什么?怎么解释结果?为什么Kaiser准则说…

2026/10/5 8:38:15 阅读更多 →
TeleOCR 文档理解引擎实战:从 OmniDocBench 榜单第一到复杂表格与多语言混排落地

TeleOCR 文档理解引擎实战:从 OmniDocBench 榜单第一到复杂表格与多语言混排落地

1. 从榜单第一说起:TeleOCR 到底解决了什么问题 OmniDocBench 这个榜单在文档解析圈子里分量不轻,它不像很多评测只盯着单一场景刷分,而是把版面分析、表格还原、公式识别、多语言混排、手写体、低质量扫描件这些真实业务里最头疼的维度全拉进…

2026/10/5 8:38:14 阅读更多 →
PSO-LSTM神经网络调整收盘价预测:超参数优化与时序建模实战

PSO-LSTM神经网络调整收盘价预测:超参数优化与时序建模实战

简介:基于PSO-LSTM神经网络的股票调整收盘价预测源码包,面向需要完成期末大作业或课程设计的高校学生,也适合刚接触深度学习时序预测的开发者。项目使用粒子群算法自动搜索LSTM最优超参数,包含数据预处理、模型搭建、训练与评估等…

2026/10/5 8:37:14 阅读更多 →

日新闻

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:22 阅读更多 →
AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

1. 从“plugins”这个词说起:它到底在解决什么问题如果你最近在折腾 AI 编程工具,尤其是 Cursor、Codex CLI、Claude Code 这类带 CLI 的编辑器或命令行助手,那你大概率绕不开一个词——plugins。这个词本身不新鲜,从浏览器到 IDE…

2026/10/5 0:00:23 阅读更多 →
第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:23 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:06:42 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:10:22 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 3:06:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:14:29 阅读更多 →