订阅更新

同样的SQL开了向量化快3倍——Doris的向量化执行引擎怎么工作的?

Doris手册 第 8 题 约 7 分钟读完

本文目录
  1. 先说结论
  2. 逐行处理 vs 向量化处理——用类比讲清楚
  3. 为什么向量化能快?——三个底层原因
  4. Doris向量化引擎的实现细节
  5. 实战经验:向量化的性能提升实测
  6. 面试高频追问
  7. 总结

先说结论

向量化执行的核心思想是:把逐行处理(Row-based)改成批量处理(Column Batch)。一次处理一批数据(比如4096行)的同一列,而不是一行一行地处理所有列。好处是:CPU缓存命中率大幅提升、可以利用SIMD指令并行计算、函数调用次数大幅减少。

效果:Doris从1.1版本起默认启用向量化引擎,同样的查询性能普遍提升2-5倍。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

向量化执行引擎是Doris 1.1+引入的核心性能优化,也是OLAP引擎面试的高频考点。面试官问这道题,不是让你说'向量化就是批量处理数据'这种泛泛的回答,而是要你说清楚:向量化和逐行处理的本质区别是什么?为什么能快3倍?底层的CPU缓存和SIMD是怎么发挥作用的?

逐行处理 vs 向量化处理——用类比讲清楚

想象你要计算1000个员工的年薪(月薪×12)。逐行处理是:拿第1个员工的月薪算年薪,记下来;拿第2个员工的月薪算年薪,记下来...重复1000次。向量化处理是:一次拿出所有1000个员工的月薪列,整列乘以12,一步完成。

维度逐行处理(Row-based)向量化处理(Vectorized)
处理单位一次处理一行的所有列一次处理一批(4096行)的一列
CPU缓存每行切换列→缓存频繁失效连续读同一列→缓存命中率高
SIMD指令无法利用可以一条指令处理多个数据
函数调用每行调一次函数→开销大每批调一次函数→开销小
虚函数开销每行都有类型判断每批只判断一次类型
典型性能基准2-5倍提升

为什么向量化能快?——三个底层原因

① CPU缓存命中率提升

CPU缓存(L1/L2/L3 Cache)的访问速度比内存快10-100倍。逐行处理时,每处理一行都要跳到不同列的内存地址,频繁发生Cache Miss。向量化处理时,同一列的数据在内存中连续存放,批量读取时CPU缓存预取机制可以把后续数据提前加载,Cache命中率大幅提升。

② SIMD指令并行计算

现代CPU支持SIMD(Single Instruction, Multiple Data)指令:一条指令同时处理多个数据。比如AVX-256指令可以一次对8个32位整数做加法。向量化引擎把同一列的数据排成连续数组,就能直接利用SIMD加速。

# SIMD示例(伪代码)
#
# 逐行处理 SUM(amount):
#   sum += amount[0]  ← 1次CPU加法
#   sum += amount[1]  ← 1次CPU加法
#   sum += amount[2]  ← 1次CPU加法
#   sum += amount[3]  ← 1次CPU加法
#   ... 重复N次
#
# 向量化 + SIMD处理 SUM(amount):
#   vec_sum = SIMD_ADD(amount[0:7])  ← 1条SIMD指令加8个数
#   vec_sum = SIMD_ADD(amount[8:15]) ← 1条SIMD指令加8个数
#   ... 循环次数减少8倍
#
# 实际效果:纯计算场景,SIMD可以快4-8倍

③ 函数调用开销减少

数据库引擎的每个操作符(Filter、Aggregate、Join等)都是函数调用。逐行处理每处理一行就要调一次函数,1000万行就调1000万次。向量化处理每4096行调一次函数,调用次数减少2000多倍,函数调用的固定开销(参数压栈、寄存器保存等)被分摊到整批数据上。

Doris向量化引擎的实现细节

-- 检查向量化引擎是否启用
SHOW VARIABLES LIKE 'enable_vectorized_engine';
-- Doris 1.1+ 默认开启
 
-- 如果查询没有走向量化(极少数情况)
SET enable_vectorized_engine = true;
 
-- 向量化引擎的批处理大小
SHOW VARIABLES LIKE 'batch_size';
-- 默认4096行/批
-- 可以调整:SET batch_size = 8192;
-- 但通常默认值就是最优的
 
-- 用EXPLAIN VERBOSE查看是否走了向量化
EXPLAIN VERBOSE SELECT country, SUM(amount)
FROM ods_user_event WHERE dt = '2024-01-15'
GROUP BY country;
-- 执行计划里会显示 'VEC' 标记(Vectorized)

实战经验:向量化的性能提升实测

在某元宇宙社交公司升级Doris到1.2版本时(从非向量化升级到向量化),我对核心查询做了AB测试:

查询类型非向量化向量化提升倍数
单表聚合(SUM/COUNT)2.5秒0.6秒4.2倍
多维GROUP BY5.3秒1.8秒2.9倍
大表JOIN小表8.2秒3.1秒2.6倍
复杂表达式过滤3.7秒1.0秒3.7倍
字符串函数(LIKE/REGEXP)6.1秒2.8秒2.2倍

可以看到,纯数值计算的加速最明显(4倍+),字符串操作的加速相对较小(2倍+)。这是因为数值类型更容易利用SIMD指令,字符串长度不固定,SIMD的适用性有限。

面试高频追问

追问一:向量化和列式存储是什么关系?

列式存储是向量化的基础。列式存储把同一列的数据连续存放在磁盘上,读取时天然形成一个连续的数组。向量化引擎拿到这个数组后批量处理,充分利用CPU缓存和SIMD。如果是行式存储,数据在内存中不连续,向量化的效果就大打折扣。所以向量化+列存是天生一对。

追问二:所有查询都能走向量化吗?

Doris 2.0+基本所有查询都走向量化了。早期版本有些复杂的UDF(用户自定义函数)或特殊数据类型不支持向量化,会退化为逐行处理。如果发现某个查询没走向量化,先检查是不是用了不支持的函数或类型。

追问三:ClickHouse的向量化和Doris有什么区别?

ClickHouse是最早把向量化做到极致的OLAP引擎,它的单表向量化性能目前仍然是最好的。Doris的向量化是后来追赶的,在单表场景下性能接近ClickHouse(差距在10-20%以内),在多表JOIN场景下Doris的MPP+向量化组合优势更大。

总结

向量化面试要讲清楚三个为什么:① 为什么批量处理比逐行快(CPU缓存命中率);② SIMD指令的作用(一条指令处理多个数据);③ 函数调用开销减少。和列式存储的关系也要提到——列存是向量化的基础,两者配合才能发挥最大威力。能说出实际的性能对比数字(2-5倍提升),面试官会觉得你不只懂理论,还有实测经验。

下一篇聊Doris的Compaction机制:为什么导入数据后查询突然变慢了?后台在做什么?

你们升级到向量化引擎后性能提升了多少?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器