SIMD与列式计算:时序数据库向量化执行引擎原理

Jing Wang

2026-10-16 /

火山模型与逐行处理

传统数据库执行引擎采用火山模型(Volcano Model,又称迭代器模型)。每个算子实现为迭代器,向上层提供next()接口返回一行数据,上层算子循环调用下一层算子的next()获取输入行。SELECT查询的执行是算子树自顶向下拉动数据、自底向上返回结果的过程。

火山模型的优点是算子解耦、流式处理、内存占用低,每个算子只需处理一行,无需物化中间结果。缺点是逐行处理的调用开销大。每次next()调用涉及虚函数分派、类型检查、边界判断,这些固定开销在单行处理时占比极高。当处理10亿行数据时,函数调用开销本身就构成主要开销。这一模型对时序数据库的聚合查询形成了明显制约。

逐行处理还阻碍了CPU的并行能力。现代CPU的流水线、超线程、SIMD向量单元都需要指令级的并行数据流才能发挥。逐行处理中,每行数据的类型和值都不同,分支预测失效,SIMD无法应用,CPU大部分时间花在分支判断和小数据处理上,指令级并行度低。

向量化执行的基本思想

向量化执行(Vectorized Execution)的核心是把”一次处理一行”改为”一次处理一批(batch)”。算子不再返回单行,而是返回一个包含多行(通常1000-10000行)的列式批数据。算子内部循环遍历批内数据完成计算。

批处理摊薄了每个算子的固定开销。函数调用、类型检查、边界判断的开销被批内所有行分摊,单行的调用开销从数纳秒降至亚纳秒。批数据以列的数组形式组织(而非行的结构体数组),连续内存布局提高了缓存命中率。

向量化的另一个收益是编译器与CPU的优化空间。批内循环是简单的数据并行循环,编译器可以自动向量化,CPU的SIMD单元可以一次处理多个数据元素。批处理还消除了逐行处理中的分支预测失败,循环内无分支,流水线效率提升。

执行模型

处理单位

调用开销

SIMD利用率

缓存效率

内存占用

火山模型

单行

高

极低

低

低

向量化

批次

低

高

高

中

编译执行

批次

最低

高

高

中

列式存储与向量化的契合

列式存储是时序数据库与向量化执行的共同基础。行式存储中,同一行的不同列在内存中连续,向量化处理某一列时需要跨行跳跃访问(步长等于行宽),无法连续读取。列式存储中,同一列的所有值在内存中连续,向量化处理某一列时只需顺序读取连续内存,缓存友好且便于SIMD加载。

时序数据的写入模式与存储结构决定了列式的必要性。时序数据以时间为序写入,同一时间线的同一指标连续排列,天然适合列式存储。查询聚合时只访问聚合涉及的列,列式存储配合列裁剪(Column Pruning)跳过多余列,扫描量降低。向量化执行在列式数据上遍历,SIMD加载连续的数据块,聚合计算的吞吐量显著提升。

过滤(Filter)是向量化的典型受益算子。过滤条件如”温度大于80℃”,在列式数据上对整列做批量比较,生成位图(Bitmap)标记命中的行,后续算子按位图选择数据。相比逐行判断,批量比较的SIMD吞吐可达每周期数十个元素。

SIMD指令集与算子实现

SIMD(单指令多数据)是CPU提供的向量计算能力。x86平台的AVX2指令为256位(一次处理8个32位整数或4个64位浮点),AVX-512为512位;ARM平台的NEON为128位,SVE为可变长。向量化执行引擎在热点算子中使用SIMD指令,单条指令处理多个数据。

以求和为例,标量循环累加每周期处理1个浮点数;AVX2的加法指令一次处理8个浮点数,理论加速8倍。实际加速受内存带宽限制,因为AVX2峰值吞吐超过内存供给速度,真正瓶颈转移到内存带宽而非计算。这也是列扫描型查询(如聚合)加速明显、随机访问型查询(如点查)加速有限的原因。

SIMD主要用于规整的数值计算——算术运算、比较、位运算、min/max、sum/count。算子需要将条件分支转换为无分支形式以适配SIMD。例如聚合”按分组键求和”,若分组键稀疏分布,SIMD难以处理;若分组数量少且可用哈希表固定映射,则可以让SIMD批量计算各分组的局部和。

指令集

位宽

float32/指令

int32/指令

平台

SSE

128位

4

4

x86

AVX2

256位

8

8

x86

AVX-512

512位

16

16

x86

NEON

128位

4

4

ARM

算子向量化:过滤、聚合与连接

过滤算子最容易向量化。谓词计算(如 col > const)在列上批量执行,输出选择向量(Selection Vector,记录命中行的下标)或位图。选择向量方式的优点是避免了数据拷贝,后续算子按选择向量下标访问数据。

聚合算子(count/sum/avg/min/max)向量化程度高。求和、最小值、最大值可以直接用SIMD的横向归约指令;平均需要求和与计数两个归约。分组聚合(GROUP BY)的向量化较复杂,通常采用”先哈希分组、再分组内向量化聚合”的两阶段策略——第一阶段按分组键计算分组ID,第二阶段对每个分组的数据向量化聚合。

连接算子(JOIN)的向量化收益相对有限,因为连接涉及随机访问与哈希探测,访问模式不规整。时序场景中的连接主要是标签表与数据表的连接(类似维表关联),标签表小且可缓存,连接的随机访问主要体现在数据表侧。

结语

向量化执行引擎的加速效果来自两方面:摊薄算子的固定调用开销,以及利用CPU的SIMD与缓存能力。它把数据库的执行效率从逐行解释执行提升到批量编译执行的层次。对于以聚合与扫描为主的时序查询,向量化的收益尤为显著——这也是近年新架构的时序数据库普遍采用向量化执行引擎的原因。TDengine的向量化查询引擎结合列式存储,使大范围聚合查询的吞吐量相比逐行引擎有明显提升,为高基数、大数据量场景下的交互式分析提供了执行层面的支撑。