土法炼钢 · 系统与基础设施

【列存引擎内核】查询读取路径

文章导航

分类入口
databasestorage
标签入口
#clickhouse#read-path#prewhere#mark-range#explain#parallel-read#24-lts

目录

SELECT 在 MergeTree 上不是「全表扫磁盘」——primary.idx 与跳数索引先定 Mark Range,再按列读 .bin 压缩块,PREWHERE 可能只读过滤列。本文串起规划器下推到存储的边界,以及 Part / 线程并行。

本环境未安装 ClickHouseEXPLAIN 示例仅给 SQL 框架,不伪造计划文本。


一、读路径总览

flowchart TB
  SQL[SQL] --> OPT[Optimizer]
  OPT --> PK[PK granule 剪枝]
  OPT --> SKIP[跳数索引剪枝]
  PK --> MR[Mark Range]
  SKIP --> MR
  MR --> PRE[PREWHERE 列读]
  PRE --> REST[其余投影列]
  REST --> BLK[Block → Pipeline]

二、Mark Range

对每个 Active Part:

  1. 读取内存中 primary.idx(或缓存)。
  2. 按 PK 谓词二分得 granule 下标区间 \([g_{lo}, g_hi}]\)
  3. 列 Mark .mrk2 同区间映射到 .bin 块。

多 Part 各自 Mark Range,结果在算子层合并(UNION ALL 语义)。


三、PREWHERE vs WHERE

PREWHERE WHERE
执行位置 存储层优先 通常 Block 上
目的 少读列 完整 predicate
写法 PREWHERE 子句或优化器自动 WHERE

官方建议:高选择性条件利于 PREWHERE(如 user_id = 123 在大表)。

实验方法(本机):对比 system.query_logread_rowsread_bytes 与是否启用 PREWHERE——须自测填数


四、并行读

flowchart LR
  Q[Query threads] --> P1[Part A Mark Ranges]
  Q --> P2[Part B Mark Ranges]
  Q --> P3[Part C Mark Ranges]
  P1 --> BLK1[Block]
  P2 --> BLK2[Block]
  P3 --> BLK3[Block]
  BLK1 --> MERGE[算子层合并]
  BLK2 --> MERGE
  BLK3 --> MERGE

五、EXPLAIN indexes=1

EXPLAIN indexes = 1
SELECT count()
FROM hits
WHERE EventDate = '2014-03-01' AND UserID = 123;

用于查看 每个 Part 的 granule 剪枝 与跳数索引使用情况。解读要点:

请在本机执行后阅读输出;本文不粘贴虚构计划。


六、与 LSM 读放大对照

LSM 多层 SST 叠加(LSM 概览);MergeTree 多 Part 叠加 + 稀疏索引。merge 降低 Part 数 = 降低读放大(第 6 篇)。


七、学术谱系:Late Materialization · PREWHERE · Mark Range

阶段 文献 / 机制 本篇落点
2008 Abadi et al., SIGMOD — late materialization 等 先过滤再取宽列;减少中间物化
CH PREWHERE 在读路径早期用部分列裁剪 granule/行
CH Mark Range + 稀疏 PK 先定 哪些 granule,再解压(第 2、7 篇)
对照 行存 volcano 早物化整行 PG 执行器

谱系结论:Abadi 2008 的「列存优势清单」在 CH 上落成 PREWHERE + 列裁剪 + 向量 Block;Mark 文件是论文少写的 工程索引,把「逻辑行号」接到压缩块偏移。

7.1 工程间隙

论文策略 ClickHouse 现实
优化器自动选 late mat 需合理 PREWHERE / 主键前缀;误用收益小
理想连续 granule 多 Part、差排序键 → 几乎全表 granule
忽略解压 压缩块解压常大于谓词 CPU(第 3 篇)

7.2 开放问题

  1. PREWHERE 自动改写是否应更激进? 可检验:同查询手写 vs 自动 PREWHERE 的读字节(入口:Abadi SIGMOD’08;CH PREWHERE)。
  2. 自适应 Mark 与固定 granularity 对剪枝稳定性的影响——与第 2 篇开放问题联动。

八、小结

读路径 = 索引定 granule → Mark 定块 → 解压列 → PREWHERE 裁剪 → Block 进 pipeline


上一篇向量化执行

下一篇Merge 与 Mutation


参考资料

核心论文

  1. Abadi, Madden & Hachem, Column-Stores vs. Row-Stores: How Different Are They Really?, SIGMOD 2008(late materialization 等;A 级)。
  2. Boncz et al., MonetDB/X100, CIDR 2005(向量批与读路径衔接;A 级)。

规范 / 源码 / 文档

  1. ClickHouse Documentation, PREWHEREEXPLAIN(A 级)。
  2. ClickHouse Source, MergeTreeSelectProcessorMergeTreeReadPool(A 级)。

Mark Range 算法(概念)

对每个 Part:

  1. primary.idx 二分/扫描,找与 PK 谓词相交的 granule 下标区间 \([g_{lo}, g_{hi}]\)
  2. 跳数索引进一步缩小区间(第 7 篇)。
  3. 对每列 Mark 文件取 \([g_{lo}, g_{hi}]\) 对应压缩块并解压。
  4. PREWHERE 列优先读,结果位图过滤后再读其余列(若优化器下推)。

PREWHERE 语义

PREWHERE 不是语法糖:优化器将选择性高的条件移到存储层,减少列 IO。官方建议高选择性条件放 PREWHERE;WHERE 其余 predicate 在内存 Block 上执行。

并行

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。

2026-06-18 · database / storage

【列存引擎内核】列存基础与 ClickHouse 架构

行存 vs 列存的带宽、压缩与向量化三角;ClickHouse Server 进程模型、线程池与 MergeTree 引擎家族地图;src/Storages 与 src/Processors 源码入口。对照 PG 行存与 LSM 写优化路径,版本锚定 ClickHouse 24.x LTS。

2026-06-18 · database / storage

【列存引擎内核】MergeTree Part 文件格式

ClickHouse MergeTree Part 目录结构:columns.txt、checksums.txt、.bin、.mrk2、primary.idx 语义,Granule 与 Mark 的定位作用,Wide/Compact 布局与 MergeTreeDataPart 源码入口。版本锚定 24.x LTS。

2026-06-18 · database / storage

【列存引擎内核】压缩与编码

ClickHouse 列压缩:LZ4、ZSTD、Delta、DoubleDelta、Gorilla 时序编码与列类型关系;CODEC 链顺序、LowCardinality 与 PG TOAST 对照。压缩比须本机实测,本文不编造倍数。

2026-06-18 · database / storage

【列存引擎内核】向量化执行引擎

ClickHouse Block 列向量 batch、IProcessor Pipeline 与 filter/project/aggregate 向量实现;对照 PostgreSQL 火山模型 ExecProcNode。源码入口 src/Processors、src/Columns。24.x LTS。


By .