数据库中的火山模型是一种经典的查询执行模型,由 Goetz Graefe 于 1994 年在《Volcano – An Extensible and Parallel Query Evaluation System》中提出,因此也被称为 迭代器模型。它的核心思想是将查询执行计划中的每一个物理操作抽象成一个独立的算子,并通过统一的接口让数据在算子之间自底向上“拉取”传递。下面我会从模型原理、算子实现、优缺点与现代演进几个层面,为你做一个全面细致的解析。
一、火山模型的运行机制
1. 统一迭代器接口
每个算子都被封装成一个迭代器,对外只暴露三个方法:
Open():初始化算子,分配资源(如内存、文件句柄),并递归调用子算子的 Open()。
Next():向上层返回一行(tuple)数据。若没有更多数据,则返回 `EOF`(End of File 标记)。这是模型的核心。
Close():清理资源,递归关闭子算子。
2. “拉取”式数据流
火山模型是 pull-based的执行方式:
执行引擎从查询计划树的根节点开始,调用根算子的 `Next()`。
根算子为了产出一行,会调用它的子算子的 `Next()`,如此层层向下调用,直到叶子节点(如全表扫描算子)从磁盘或内存中读取一行原始数据。
数据再沿调用栈逐层向上返回,每经过一个算子就会被加工一次(过滤、投影、连接等),最终到达根节点输出给客户端。
这种一拉到底,再逐级传回的方式,很像火山喷发时岩浆从地底逐层上升,故称火山模型。
3. 一次一行的处理粒度
经典火山模型的 `Next()` 每次只返回一个元组,算子也每次只处理一个元组。这使得内存占用极低,逻辑清晰,但函数调用次数非常多(百万行数据就有百万次虚函数调用),这也是它后来被向量化模型替代的主要原因。
二、火山模型的优缺点
优点
简洁与可组合:
所有算子接口相同,任意复杂查询都可通过搭建一棵算子树实现,扩展新算子只需实现三个接口。
流式处理,内存节约:
非阻塞算子可以边读边处理,不需要缓存大批数据,适合处理海量数据集。
易于实现流水线并行:
只要解决上下文切换问题,多线程可自然形成生产者-消费者流水线。
中断/取消天然支持:
只要在 `Next()` 中检查中断标志并返回 `EOF` 即可优雅停止查询。
缺点
虚函数开销巨大:
每处理一行都要经历从根到叶的多次虚函数调用,CPU 分支预测频繁失败。
Cache 与 SIMD 不友好:
一次一行的模式使得代码和数据局部性很差,难以利用 CPU 的向量化指令(SIMD)批量处理。





