第4章 执行器与算子实现

执行器是数据库内核的“发动机”,负责按照计划树驱动算子拉取数据并计算。面试中常围绕火山模型、向量化执行、Join算法和聚合算子展开,重点考察对逐行处理与批量处理差异、不同Join策略取舍、内存管理与性能优化的理解。本章通过三个问答卡片和一个实战项目模板,帮你快速掌握核心考点。

5.1 火山模型 vs 向量化执行框架

火山模型 (Volcano) 逐行调用 next() 每个算子一次返回一行 管道模型,线性流水 实现简单,通用性强 特点:逐行,虚函数开销大 演进 向量化执行 批量读取列式数据 一次处理一批(如1024行) CPU Cache友好 减少虚函数调用 特点:批量,数据驱动

面试问答卡片

Q1:请解释火山模型的工作原理及其优缺点。

面试官考察点:对经典执行器模型的理解,以及能否指出其性能瓶颈。

回答框架:

  1. 给出火山模型定义与核心接口(next())
  2. 说明算子间的管道化协作
  3. 列出优点:实现简单、易扩展
  4. 指出缺点:逐行虚函数调用、CPU cache不友好、无法利用SIMD

示例回答:火山模型基于迭代器模式,每个算子通过next()逐行返回数据。优点在于算子实现独立,便于添加新算子;缺点是每行数据都经过多次虚函数调用,导致CPU分支预测失败率高,且无法利用SIMD指令,成为现代OLAP引擎的主要瓶颈。

可能追问:火山模型在MPP架构下如何优化?能否用编译执行替代?如何减少next()调用次数?

避坑提醒:不要只说“逐行处理效率低”,要具体解释虚函数开销、缓存不命中、以及与现代硬件(SIMD、多核)的冲突。

Q2:向量化执行如何提升性能?请结合具体算子说明。

面试官考察点:对列式存储与批量处理的理解,能否延伸到实际算子优化。

回答框架:

  1. 解释向量化执行的核心思想(批量处理 + 列式内存布局)
  2. 对比火山模型,说明减少虚函数、提升数据局部性
  3. 举例:Hash Join中批量计算hash值、过滤算子中利用SIMD做谓词判断
  4. 提及实现难点(变长字段处理、向量化函数库)

示例回答:向量化执行将数据组织为列式批量(如1024行),算子内部用向量化函数一次处理整批。例如聚合算子中,对整数列用SIMD指令并行累加,相比逐行累加提速5-10倍。同时虚函数调用次数降低至1/1024,CPU流水线更稳定。

可能追问:向量化执行对内存管理有何要求?如何支持变长字符串?ClickHouse和DuckDB的实现差异?

避坑提醒:避免只说“批量处理更快”,要提到列式布局、CPU缓存行利用、SIMD指令集(SSE/AVX)等底层机制。

Q3:请比较Hash Join、Merge Join与Nested Loop Join的适用场景与优缺点。

面试官考察点:对常见Join算法复杂度、内存模型、数据分布敏感度的掌握。

回答框架:

  1. 简要介绍三种Join的核心思想
  2. 从时间复杂度、内存需求、排序要求、连接键类型等维度对比
  3. 给出典型选型建议:等值且小表可全内存→Hash Join;有序大表→Merge Join;非等值或数据量极小→Nested Loop

示例回答:Hash Join适合等值连接且内表可放入内存,复杂度O(N+M),但需Hash表构造时间;Merge Join需两表已排序,适合有序场景;Nested Loop Join实现简单,但最坏O(N*M),适用于小表或非等值连接。实际引擎常根据统计信息自动切换。

可能追问:Grace Hash Join如何处理内存不足?Bloom Filter如何优化Hash Join?Hybrid Hash Join的实现细节?

避坑提醒:不要直接说“Hash Join最快”,要强调内表大小、内存限制;也要注意Merge Join在外表有序但内表无序时需要排序,可能整体代价更高。

5.2 Join算法面试追问路径导图

(从Hash Join出发,面试官常沿着内存、数据倾斜、优化手段等方向追问)

Hash Join 内表太大放不下? Grace Hash Join Hybrid Hash Join 如何减少按键匹配开销? Bloom Filter 过滤 分区/桶裁剪 向量化Hash 热点Key倾斜? 再分区 / 广播

5.3 项目案例:OLAP引擎Join算子向量化改造

维度内容
项目名称StarRocks 向量化Hash Join优化
业务背景实时OLAP报表查询,TPC-H 10TB测试集中Join占总耗时40%,需降低延迟。
规模指标单表最大50亿行,Join操作涉及列最大20列,QPS峰值2000+。
技术栈C++17、SIMD (AVX2)、内存池、SIMD JSON parser、Presto 向量化函数库
核心难点Hash表多线程并发写入冲突;变长字符串在向量化batch中的高效存储;内外表数据倾斜导致个别线程超时。
面试高频追问如何设计支持变长字段的Hash表?Grace Hash Join分桶策略如何选择?Bloom Filter参数如何确定?向量化Hash函数实现细节?
可迁移回答话术“我们采用分桶+并行构建的方式,外层用粗粒度分区避免写冲突,内层用线程本地缓存合并,最终将Join算子延迟从800ms降至120ms。变长字符串通过 offset+data 双数组存储,在SIMD处理前先做长度对齐。”
💡 面试加分提示:在描述项目时,主动提及火山模型到向量化执行的演进动机(性能瓶颈),以及针对数据倾斜时采用的“skew join”或“随机分桶”方案,能体现对实战问题的深入理解。