存储层把 Part 读成 列向量(第 2 篇);执行层必须在 batch 上算,否则函数调用与 cache miss 吃掉列存带宽优势。ClickHouse 官方 Architecture 明确:operations dispatch on arrays (vectors or chunks of columns)。
本文拆解 Block、IProcessor pipeline,并对照
PG
执行器火山模型。版本:24.x LTS。
一、为什么需要向量化
Volcano:每算子 next() 返回一行 → 虚函数 +
分支多,CPU 流水线难满。
向量:一次处理 \(N\)
行(max_block_size,常见 65536)→ filter
生成位图、aggregate 批量 update。
MonetDB/X100(CIDR 2005)系统论证了 vectorized + cache-conscious 对 OLAP 的必要性;ClickHouse 属同一设计谱系。
flowchart LR
VOL[Volcano 逐行] --> CH[ClickHouse 逐 Block]
CH --> SIMD[SIMD 友好]
二、Block 与 Column
Block(src/Core/Block.h)= 列名
+ ColumnWithTypeAndName 数组 + 行数。
| 类型 | 头文件 | 说明 |
|---|---|---|
ColumnVector<T> |
ColumnVector.h |
数值 POD 数组 |
ColumnString |
ColumnString.h |
偏移 + chars |
ColumnNullable |
ColumnNullable.h |
null map + nested |
ColumnLowCardinality |
ColumnLowCardinality.h |
字典列 |
IColumn 接口:clone、insert、filter、permute
等,算子间传递 ColumnPtr。
三、IProcessor 与 Pipeline
IProcessor(Processors/IProcessor.h):
prepare():声明需要输入端口 / 输出就绪work():消费 Chunk,产生 Chunk- 端口连接构成 DAG
PipelineExecutor 多线程调度
ready 处理器,减少全局同步(官方
Architecture Processors 节)。
flowchart TB
SCAN[MergeTreeSelect] --> PRE[Prewhere]
PRE --> FIL[FilterTransform]
FIL --> EXP[ExpressionTransform]
EXP --> AGG[AggregatingTransform]
AGG --> OUT[Output]
四、读存储:MergeTreeSource
MergeTreeSelectProcessor / read pool 从 Part
读列 → 组装 Block → 下游算子。与 第 5
篇 衔接。
五、Filter 与 Project
FilterTransform:对条件列求值 →
UInt8 过滤 mask → IColumn::filter
裁剪各列。
ExpressionTransform:ExpressionActions
将 AST 编译为对列的循环(非常量折叠时)。
六、Aggregate
AggregatingTransform:按 key 列
hash,批量更新 AggregateFunction 状态(非逐行
transition)。GROUP BY +
高基数时内存受
max_bytes_before_external_group_by 等限制。
七、与 PG 执行器对照
| 机制 | PostgreSQL | ClickHouse |
|---|---|---|
| 驱动 | ExecutorRun 循环
ExecProcNode |
PipelineExecutor |
| 数据 | TupleTableSlot |
Block |
| 表达式 | EEO opcode | ExpressionActions |
| Hash Join | 逐行 build/probe | 批量 |
| 并行 | parallel worker 部分节点 | pipeline + read pool |
PG 向量化改进(如 bulk read)存在,但 OLTP 兼容约束下仍以行为主;ClickHouse 默认即 batch。
八、Settings 与调优
| Setting | 含义 |
|---|---|
max_block_size |
单 Block 行数上限 |
min_insert_block_size_rows |
insert 最小 batch |
max_threads |
执行线程 |
compile_expressions |
LLVM JIT 表达式(视版本) |
九、实验说明
本环境无 ClickHouse。读者可用:
EXPLAIN PIPELINE SELECT count() FROM table WHERE ...;观察 processor 链名称。不粘贴未执行输出。
十、学术谱系:Volcano → MonetDB/X100 → Processors
| 阶段 | 文献 / 系统 | 数据单元 | 要点 |
|---|---|---|---|
| 1994 | Graefe, Volcano | 一行 / next() |
可扩展迭代器;虚调用与分支多 |
| 2005 | Boncz et al., MonetDB/X100, CIDR | 向量批 | cache-conscious;打满流水线与 SIMD |
| 2018 | Kersten et al., Compiled and Vectorized Queries, VLDB | 编译算子 vs 解释向量 | 无绝对赢家:查询形状、编译摊销、缓存行为决定 |
| 2010s+ | ClickHouse IProcessor /
Block |
数千~数万行 Block | 默认向量化;可选 compile_expressions
JIT |
谱系结论:第 1 篇已钉 C-Store
存储侧;本篇钉执行侧——CH 主路径是 X100
式向量解释,不是 HyPer 式全查询
codegen。compile_expressions 是局部加速,不改变
Pipeline 以 Block 为边界的模型。
10.1 争论:Compiled vs Vectorized
| 立场 | 代表 | 主张 |
|---|---|---|
| 向量解释优先 | X100 / VectorWise 传统;CH 默认 | 批处理已够满流水线;无编译延迟;易调试 |
| 编译优先 | HyPer 等 | 去掉解释循环,紧循环 + 特化类型 |
| 实证调和 | Kersten et al., VLDB 2018 | 两者在不同算子/硬件上互有胜负;混合常见 |
工程上:CH 用向量 Block 保可观测与多租户公平;JIT 仅覆盖热点表达式——与「全计划 LLVM」不是同一设计点。
10.2 工程间隙
| 论文设定 | ClickHouse 24.x |
|---|---|
| 单查询独占机器 | 多查询共享 max_threads、内存 tracker |
| TPC-H 长 scan | 短交互查询 + 持续 merge 抢 CPU |
| 理想列向量对齐 | Nullable / LowCardinality / 字符串偏移使 autovec 不总触发 |
| 少谈存储读 | MergeTreeSource 解压 granule 常成瓶颈(第 5
篇) |
10.3 开放问题
- CH 何时值得默认打开更激进的 codegen?
可检验:同查询开关
compile_expressions的 CPU 与首次延迟(入口:Kersten VLDB’18;CH settings 文档)。 - Block 大小是否应随算子自适应? 固定
max_block_size对 filter 选择性极端时可能浪费;社区有讨论,无统一论文结论。 - 与 DuckDB morsel 的并行模型谁更适合多核 skew? 见第 12 篇。
十一、小结
列存执行核心:Block 列向量 + Processor DAG + 批量算子。学术坐标:Volcano → X100 →(Kersten 争论)→ CH Processors。读路径产出 Block,算子链在 Block 上向量化,直到输出客户端。
上一篇:压缩与编码
下一篇:查询读取路径
参考资料
核心论文
- Graefe, Volcano—An Extensible and Parallel Query Evaluation System, IEEE TKDE 1994(A 级)。
- Boncz et al., MonetDB/X100: Hyper-Pipelining Query Execution, CIDR 2005(A 级)。
- Kersten et al., Everything You Always Wanted to Know About Compiled and Vectorized Queries But Were Afraid to Ask, PVLDB 2018(争论;A 级)。
规范 / 源码 / 文档
- ClickHouse Documentation, Architecture Overview(Processors / Block;A 级)。
- ClickHouse Source, v24.3,
src/Processors/、src/Columns/、src/Core/Block.h(A 级)。 - PostgreSQL 系列, 执行器(Volcano 对照)。
Block 与 Chunk
Block(Core/Block.h)含多列;Pipeline
中 Chunk 携带 Block 或列子集 +
行数。max_block_size 控制单行算子输出规模。
IProcessor 状态机
prepare() 返回 NeedData /
Ready /
Finished;work() 消费输入
Chunk、产出输出 Chunk。PipelineExecutor
多线程调度 ready 节点,避免全局锁(官方 Architecture
Processors)。
与 PG 执行器对照
| PG | ClickHouse |
|---|---|
ExecProcNode 拉一行 |
IProcessor::work 拉一批 |
TupleTableSlot |
Block / ColumnVector |
ExprState EEO |
ExpressionActions 列循环 |
nodeHashjoin.c 逐行 probe |
HashJoin transform 批量 |
SIMD
Columns/Common/Simd* 与函数实现(如
FunctionsArithmetic)对固定宽度类型使用
intrinsics;具体是否触发取决于类型与编译标志(-march)。
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【列存引擎内核】列存基础与 ClickHouse 架构
行存 vs 列存的带宽、压缩与向量化三角;ClickHouse Server 进程模型、线程池与 MergeTree 引擎家族地图;src/Storages 与 src/Processors 源码入口。对照 PG 行存与 LSM 写优化路径,版本锚定 ClickHouse 24.x LTS。
【列存引擎内核】MergeTree Part 文件格式
ClickHouse MergeTree Part 目录结构:columns.txt、checksums.txt、.bin、.mrk2、primary.idx 语义,Granule 与 Mark 的定位作用,Wide/Compact 布局与 MergeTreeDataPart 源码入口。版本锚定 24.x LTS。
【列存引擎内核】压缩与编码
ClickHouse 列压缩:LZ4、ZSTD、Delta、DoubleDelta、Gorilla 时序编码与列类型关系;CODEC 链顺序、LowCardinality 与 PG TOAST 对照。压缩比须本机实测,本文不编造倍数。
【列存引擎内核】查询读取路径
MergeTree SELECT 读路径:Mark Range 定位 Granule、PREWHERE 与 WHERE、Part 级并行与 max_threads。EXPLAIN indexes=1 解读方法。24.x LTS,无伪造 EXPLAIN 输出。