8. Doris 系列第8篇:查询引擎深度解析|MPP+Pipeline+向量化,从SQL解析到执行全流程

张开发
2026/5/21 15:26:55 15 分钟阅读
8. Doris 系列第8篇:查询引擎深度解析|MPP+Pipeline+向量化,从SQL解析到执行全流程
适合人群大数据开发、Doris查询调优工程师、OLAP引擎运维、数仓架构师核心价值吃透Doris查询引擎底层架构、SQL执行全流程掌握RBO/CBO优化、向量化执行核心技巧彻底解决查询慢、资源浪费难题系列说明本文是Doris进阶系列第8篇承接上篇数据导入全方案聚焦查询引擎核心能力基于Doris 2.x最新架构全程纯生产干货技术细节无删减兼顾原理与实战一、开篇核心查询引擎是Doris高性能的“核心引擎”前面我们打通了Doris的架构、数据模型、存储引擎与数据导入链路而查询引擎是承接“数据存储”与“业务查询”的关键枢纽。作为实时OLAP数据库Doris查询引擎的设计直接决定了查询延迟、并发能力与资源利用率——它既要让简单点查达到亚秒级响应也要让复杂多表Join、大表聚合可行且高效。本文将从查询引擎的整体架构出发拆解SQL从解析、优化到分布式执行的全流程深入剖析MPP并行、Pipeline引擎、向量化执行三大核心技术搭配调优实践与未来演进方向帮你从根源理解Doris查询高性能的底层逻辑轻松搞定查询调优与故障排查。1. 整体架构查询引擎在Doris中的位置Doris 采用 FEFrontend BEBackend分离架构查询引擎的核心能力分散在FE与BE中分工明确、协同高效FE前端负责 SQL 解析、语义分析、查询改写、优化器RBO/CBO、物理计划生成与分片BE后端负责 物理计划执行、数据扫描、分布式计算、结果汇总核心查询流程全程无冗余关键步骤标亮Client SQL → FE: Parser语法解析→ FE: Rewriter查询改写→ FE: OptimizerRBO/CBO优化→ FE: Plan Fragmenter计划分片 → BE: 并行执行Fragment → 结果汇总返回Client✅ 设计核心思想计算下推尽可能将计算放在数据所在BE 数据本地化减少跨节点网络传输 向量化执行提升CPU利用率2. 查询处理全流程详解从SQL到结果一步不落2.1 语法解析Parsing输入客户端提交的SQL字符串兼容MySQL协议降低使用成本工具基于ANTLR4构建的自定义语法解析器适配Doris特有语法输出抽象语法树AST如SelectStmt、InsertStmt等结构化对象示例演示SQLSELECT user_id, SUM(clicks) FROM logs WHERE dt 2025-03-04 GROUP BY user_id;→ 解析后生成SelectStmt对象包含核心节点SelectListuser_id、SUM(clicks)、FromClauselogs表、WherePredicatedt过滤条件、GroupByClauseuser_id分组核心特点完全兼容MySQL常用SQL语法降低迁移成本支持Doris特有语法如ROUTINE LOAD、ROLLUP、物化视图查询严格校验关键字、函数、表名、列名的合法性提前拦截语法错误。2.2 语义分析Analysis核心职责将抽象语法树AST转换为带元数据信息的逻辑执行计划Logical Plan消除无效操作。核心任务重中之重直接影响查询性能Catalog解析查询元数据确认表/视图是否存在、列类型是否匹配、分区/分桶信息是否有效表达式绑定将SQL中的列如user_id、函数如SUM绑定到具体表的元数据避免歧义类型推导自动推导表达式返回类型如SUM(clicks)若为INT类型返回BIGINT避免溢出权限校验检查当前用户是否有该表的SELECT权限无权限直接返回报错分区裁剪Partition Pruning根据WHERE dt 2025-03-04直接过滤掉非目标分区减少90%以上无效I/O桶裁剪Bucket Pruning若user_id是分桶键可直接定位到目标Tablet避免全表扫描。输出Analyzed Logical Plan带元数据、权限、裁剪信息的逻辑计划树 关键结论此阶段是“减少无效I/O”的核心环节分区/桶裁剪配置合理可直接将查询耗时降低一个数量级。2.3 查询改写Query Rewriting目的对逻辑计划进行等价变换不改变查询结果提升后续优化器的优化空间降低执行成本。核心驱动由RuleExecutor规则引擎驱动内置多种改写规则支持自定义扩展。常用改写规则生产高频用到改写规则核心说明示例谓词下推Predicate Pushdown将WHERE过滤条件尽可能下推到Scan节点提前过滤无效数据减少后续计算量SELECT * FROM (SELECT a,b FROM t) WHERE a10→SELECT a,b FROM t WHERE a10Project下推列裁剪只读取查询涉及的列不读取无关列减少内存占用和I/O开销SELECT user_id FROM logs→ 仅扫描user_id列不扫描其他列子查询去关联Decorrelation将相关子查询转为Join操作避免嵌套循环提升执行效率相关子查询SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.idt2.id)→ 转为t1 JOIN t2 ON t1.idt2.id物化视图匹配自动识别查询是否可复用预聚合物化视图避免重复聚合计算查询SUM(clicks)时自动匹配预聚合的物化视图直接返回结果Aggregate合并合并连续的GROUP BY操作减少中间结果量SELECT user_id FROM (SELECT user_id, SUM(clicks) FROM logs GROUP BY user_id) t→ 合并为SELECT user_id FROM logs GROUP BY user_id2.4 查询优化器RBO与CBO协同工作Doris 2.x核心升级Doris 采用混合优化器Hybrid Optimizer以RBO基于规则优化为主、CBO基于代价优化为辅兼顾优化效率与优化效果适配不同复杂度的查询场景。2.4.1 RBORule-Based Optimization基于规则优化触发时机所有查询必经RBO优化无门槛、开销低核心优势确定性高不会因数据分布变化导致优化结果波动特别适合OLAP场景的固定查询模式核心优化规则生产重点关注Join ReorderingJoin顺序调整基于Left-Deep Tree结构按表大小排序小表在左优先使用Broadcast Join减少Shuffle数据量Join算法选择Broadcast Join右表大小 100MB默认阈值将小表广播到所有BE避免ShuffleShuffle Join大表Join按Join Key Shuffle数据实现分布式并行JoinColocation Join同分桶、同分区的表直接在本地BE进行Join无跨节点数据传输Aggregate下推将聚合操作下推到Scan节点附近提前聚合减少中间结果量TopN下推将LIMIT N条件下推到执行链路前端限制中间结果行数减少内存占用。2.4.2 CBOCost-Based Optimization基于代价优化【Doris 2.0 强化】启用条件需手动开启参数enable_cost_based_join_reorder true默认关闭核心逻辑基于数据统计信息计算不同执行计划的“代价”CPU、I/O、网络开销选择代价最小的计划核心组件统计信息收集通过ANALYZE TABLE logs;收集表的关键统计信息行数、列的NDV基数、Min/Max值、直方图实验性代价模型综合计算三类代价——CPU Cost函数计算复杂度、I/O Cost预计扫描行数×列数、Network CostShuffle数据量优化算法采用动态规划DP枚举所有可能的Join顺序选择代价最小的执行计划。当前局限与最佳实践局限统计信息需手动收集自动收集功能在规划中直方图支持有限对数据倾斜场景的代价估计不够精准✅ 最佳实践简单查询单表、少量过滤依赖RBO即可复杂多表Join3表及以上建议开启CBO并提前执行ANALYZE TABLE更新统计信息。3. MPP 执行引擎分布式物理计划生成与并行调度FE完成查询优化后会将优化后的逻辑计划转换为物理执行计划并通过“计划分片”实现分布式并行执行——这是Doris作为MPP架构OLAP数据库的核心优势。3.1 Plan Fragmentation计划分片核心逻辑FE将物理计划切分为多个PlanFragment计划分片每个Fragment可独立在BE上执行通过ExchangeNode数据交换节点实现Fragment间的数据传输。关键概念ExchangeNode表示数据Shuffle的节点负责不同Fragment间的数据分发如Shuffle Join、全局聚合的数据汇总Fragment树所有Fragment构成一棵执行树根Fragment负责汇总最终结果叶子Fragment负责数据扫描。示例两表Join的Fragment拆分Fragment 0 (Root根分片) └─ MERGE SORT汇总全局结果 Fragment 1聚合分片 └─ AGGREGATE全局聚合 └─ EXCHANGE接收Fragment 2和3的Shuffle数据 Fragment 2表A扫描分片 └─ SCAN table A → SHUFFLE by join key按Join键Shuffle数据 Fragment 3表B扫描分片 └─ SCAN table B → SHUFFLE by join key按Join键Shuffle数据3.2 并行调度机制FE通过Thrift BRPC协议将各个Fragment发送到对应的BE节点优先发送到数据所在BE实现数据本地化每个Fragment实例绑定到特定Tablet所在的BE节点避免跨节点读取数据减少网络开销BE接收Fragment后启动Pipeline EngineDoris 2.0 核心执行引擎执行任务实现多线程并行。4. 多机多核并行执行模型三级并行极致利用硬件资源Doris查询引擎支持节点间、节点内、指令级三级并行充分利用多机多核CPU资源最大化查询吞吐量与响应速度。并行层级核心说明实现方式核心收益节点间并行Inter-node多BE节点并行处理不同Tablet的数据实现分布式计算MPP架构 Shuffle/Colocation Join突破单节点性能瓶颈支持海量数据并行处理节点内并行Intra-node单个BE节点内多线程并行处理一个Fragment的不同任务Pipeline执行引擎提升单节点CPU利用率减少任务阻塞向量化并行SIMD指令级单个CPU指令同时处理多条数据实现指令级并行向量化执行引擎 AVX2/AVX-512指令提升单线程处理效率吞吐提升3~10倍重点Pipeline 执行引擎Doris 2.0 重大升级传统Volcano执行模型Pull模式的痛点算子间通过“逐行拉取”数据函数调用开销大、CPU利用率低难以适配高并发场景。Doris Pipeline执行模型的优化的核心将传统算子拆分为可串联的Operator Chain算子链减少算子间的函数调用开销数据以BatchChunk为单位在算子链中流动默认一个Chunk包含4096行数据批量处理提升效率支持异步I/O 计算重叠I/O读取数据的同时CPU进行计算减少等待时间。✅ 核心效果相比传统模型CPU利用率提升30%~50%尤其适合高并发点查、大表扫描场景。5. 向量化执行引擎Doris高性能的核心支柱向量化执行是Doris查询引擎高性能的关键其核心思想是“批量处理数据”替代传统的“逐行处理”最大化利用CPU的SIMD指令集。5.1 基本原理对比直观理解传统行式处理低效// 逐行处理每行一次分支预测、函数调用CPU开销大for(row:rows){if(row.a10)output(row);}向量化处理高效// 一次处理4096行一个Chunk利用SIMD指令并行计算Column abatch.column(a);// 按列获取数据列式存储优势NullAwareBoolColumn filterVectorizedFunction::gt(a,10);// 并行比较Batch resultbatch.filter(filter);// SIMD指令加速过滤5.2 核心技术生产调优重点关注核心技术说明核心价值Columnar Batch数据按列组织每列封装为IColumn对象如ColumnInt64、ColumnString批量传递适配列式存储减少数据拷贝提升缓存命中率SIMD 指令利用CPU的AVX2/AVX-512指令集并行执行比较、计算、过滤等操作如vpcmpgtd并行比较指令单线程处理效率提升数倍降低CPU开销延迟物化Late Materialization先基于编码后的列如字典编码ID计算过滤条件筛选出有效行后再物化所需列解码为原始数据减少内存带宽占用避免无效解码操作字典编码向量化低基数字符串如性别、状态采用字典编码直接在编码ID上进行运算避免解码为字符串提升效率降低字符串处理开销提升聚合、过滤性能5.3 支持的向量化算子生产常用扫描类Scan谓词下推向量化、列裁剪向量化过滤类Filter布尔表达式向量化如a10 AND b20投影类Project表达式计算向量化如ab、SUBSTR(name,1,3)、DATE_ADD(dt,1)聚合类SUM、COUNT、MIN/MAX、AVG全向量化支持Join类Hash Join的Build/Probe阶段全向量化提升Join吞吐量。 性能收益TPC-H测试显示向量化执行相比非向量化查询吞吐提升3~10倍延迟降低50%以上。5.4 向量化内存管理Arena内存池Chunk数据统一申请、统一释放避免频繁malloc/free减少内存碎片零拷贝传递Batch在算子间通过引用传递不进行数据拷贝减少内存带宽占用。6. 查询执行全流程示例实战视角一看就懂以生产中高频查询为例SELECT user_id, SUM(clicks) FROM logs WHERE dt2025-03-04 GROUP BY user_id完整执行流程如下第一步FE处理SQL解析→优化→计划分片语法解析将SQL解析为SelectStmt抽象语法树校验语法合法性语义分析绑定logs表元数据推导SUM(clicks)返回BIGINT类型通过权限校验裁剪dt2025-03-04对应的分区定位目标Tablet查询改写将WHERE条件下推到Scan节点裁剪user_id、clicks两列避免读取无关列查询优化RBO优化——将Aggregate聚合操作下推到BE选择合适的并行度若开启CBO结合统计信息估算扫描行数优化聚合顺序计划分片生成4个Fragment——根Fragment汇总结果、聚合Fragment全局聚合、两个Scan Fragment分别扫描目标Tablet。第二步BE执行并行执行→数据处理→结果汇总FE将Fragment发送到对应BE优先发送到logs表目标Tablet所在的BE实现数据本地化BE启动Pipeline引擎并行执行各个FragmentScan算子读取Tablet的Segment文件向量化过滤dt2025-03-04输出(user_id, clicks)的Chunk数据Local Agg算子通过Hash Table向量化聚合计算每个user_id的局部SUM(clicks)输出局部聚合结果Exchange算子将局部聚合结果按user_idShuffle到负责全局聚合的BEFinal Agg算子合并所有局部聚合结果得到全局SUM(clicks)根Fragment汇总全局结果通过BRPC返回给FE。第三步结果返回FE接收BE返回的结果通过MySQL协议将最终结果user_id、SUM(clicks)返回给客户端查询完成。7. 生产查询性能调优关键点实战必看调优方向核心建议具体操作统计信息优化提升CBO优化准确性避免不合理执行计划对大表、高频查询表定期执行ANALYZE TABLE 表名;建议每天凌晨执行Join优化减少Shuffle数据量提升Join效率1. 小表100MB用Broadcast Join大表确保分桶键与Join键一致2. 同分桶表启用Colocation Join避免跨节点Shuffle向量化优化确保查询走向量化执行避免退化为行式1. 避免使用非向量化UDF自定义函数2. 调整vector_chunk_size默认4096行可根据CPU核心数调整为8192并行度优化合理分配并行资源避免资源竞争调整parallel_fragment_exec_instance_num默认1根据BE节点数、CPU核心数调整建议为CPU核心数的1/2~1/3内存优化提升向量化执行效率避免OOM1. 增大vector_chunk_size提升SIMD效率2. 调整memory_limit_per_query单查询内存限制避免单查询占用过多内存扫描优化减少无效扫描提升扫描效率1. 合理设计分区/分桶确保分区裁剪、桶裁剪生效2. 对高频过滤列建立BloomFilter索引8. 未来演进方向Doris 2.x 重点规划Adaptive Query ExecutionAQE自适应查询执行运行时动态调整执行计划如检测到数据倾斜时自动切换Join算法、调整并行度GPU加速集成CUDA利用GPU的并行计算能力加速聚合、Join、排序等计算密集型操作编译执行Compilation将SQL表达式编译为LLVM IR减少解释执行开销进一步提升执行效率智能索引推荐结合查询模式自动分析高频过滤列推荐创建BloomFilter、Bitmap等索引降低用户调优成本多源查询优化强化Multi-Catalog联邦查询能力优化跨数据源Iceberg/Hudi/Hive的查询性能。文末福利本文是Doris实战系列第8篇从SQL解析到向量化执行全覆盖查询引擎核心原理与调优技巧日常做查询调优、故障排查直接对照就能落地后续持续更新Doris查询调优参数大全、常见查询报错排错手册、复杂Join场景实战优化。

更多文章