Doris 的向量化执行能力与其 分布式架构 和 复杂查询优化 深度结合,通过 批处理 + 列式计算 + 分布式调度 的协同设计,解决传统分布式数据库在复杂查询场景下的性能瓶颈。以下是具体原理展开:
一、向量化如何适配分布式架构?
Doris 的分布式架构(MPP)天然依赖数据分片和并行计算,而向量化通过以下机制与其深度协同:
1. 数据分片与批处理的结合
-
数据分片存储:数据按分区、分桶规则分布在不同节点,每个分片(Tablet)内部数据按列存储。
-
向量化批处理单元:每个节点在处理分片数据时,以固定大小的数据块(如4096行)为单位加载到内存,按列批量处理。
-
优势:
-
减少跨节点网络传输次数(批量传输代替逐行传输)。
-
单节点内利用 CPU 缓存局部性,减少内存随机访问。
-
2. 分布式任务的向量化调度
-
Pipeline 并行执行:将查询拆分为多个阶段(Scan、Filter、Join、Aggregation),每个阶段以向量化批次为单位处理,形成流水线。
-
动态资源分配:根据数据批次的处理速度,动态调整节点间的任务分配,避免单个节点成为瓶颈。
-
示例:
一个聚合查询会拆分为:复制
扫描分片数据(向量化批次)→ 过滤(按列批量计算)→ 网络传输聚合中间结果(按批次压缩)→ 全局聚合(向量化计算)
3. 网络传输优化
-
列式压缩传输:数据在节点间传输时,按列进行压缩(如字典编码、RLE),减少网络带宽占用。
-
批处理传输:单次传输一个数据块(如4096行),而非逐行传输,降低序列化/反序列化开销。
二、向量化如何优化复杂查询?
复杂查询(如多表 JOIN、子查询、窗口函数)需要大量中间计算和内存操作,Doris 的向量化通过以下方式优化:
1. 列式计算减少冗余开销
-
按列批量处理:在 JOIN 或聚合时,直接操作整列数据,避免逐行解析字段。
-
内存连续访问:列式数据在内存中连续存储,结合 SIMD 指令(如 AVX-512)加速计算。
例如:计算WHERE age > 30
,直接对整列age
数据批量比较。
2. 动态优化执行计划
-
运行时过滤下推:在向量化处理过程中,动态将过滤条件(如 Bloom Filter)下推到扫描层,提前减少需处理的数据量。
-
JOIN 顺序调整:根据实时统计信息(如中间结果的数据量),动态选择最优的 JOIN 顺序,减少中间数据集大小。
3. 高效内存管理
-
批处理内存池:为每个批次分配连续内存空间,减少内存碎片。
-
延迟物化:在数据流转过程中,尽量保留列式结构,仅在实际需要时转换为行格式(如最终输出结果)。
4. 复杂算子优化
-
向量化 JOIN:
-
使用 Hash Join 时,批量构建哈希表,按批次匹配右表数据。
-
支持 Broadcast Join 和 Shuffle Join,按数据分布选择最优策略。
-
-
向量化聚合:
-
聚合操作(如 SUM、GROUP BY)直接对列数据批量计算,利用 SIMD 加速。
-
支持两级聚合(本地聚合 + 全局聚合),减少数据传输量。
-
5. 预聚合与物化视图
-
预计算加速:通过物化视图提前按维度聚合数据,查询时直接命中预聚合结果(按列批量读取)。
-
动态分区裁剪:利用元数据统计信息,跳过无需扫描的分区(如时间范围过滤)。
三、对比非向量化的分布式数据库
若 Doris 不使用向量化执行,在复杂查询场景下会面临以下问题:
-
CPU 利用率低:逐行处理导致大量分支预测失败,无法利用 SIMD 指令。
-
网络开销大:逐行传输数据增加序列化成本和网络往返次数。
-
内存压力大:中间结果逐行生成,占用更多内存且缓存不友好。
总结:Doris 向量化与分布式架构的协同效应
设计机制 | 解决的问题 | 性能提升 |
---|---|---|
列式批处理 + 内存连续访问 | 减少 CPU 分支预测失败、缓存失效 | 单节点计算效率提升 3-5 倍 |
流水线并行 + 动态调度 | 避免节点空闲等待,提升集群利用率 | 复杂查询延迟降低 50% 以上 |
列式压缩传输 | 减少网络带宽占用 | 网络传输开销减少 60%-80% |
预聚合 + 动态过滤下推 | 减少冗余数据处理 | 扫描数据量降低 90%(依赖查询模式) |
通俗类比
想象一个快递仓库(Doris 集群):
-
非向量化:工人(CPU)每次处理一个包裹(单行数据),频繁往返仓库和卡车(网络传输),效率低下。
-
向量化:工人使用大型集装箱(批量数据块),用叉车(SIMD 指令)一次性搬运多个包裹,同时仓库调度系统(分布式优化)动态规划最优路线,最终大幅提升吞吐量。