首页
/ 3步攻克查询性能瓶颈:Apache Doris执行计划深度优化指南

3步攻克查询性能瓶颈:Apache Doris执行计划深度优化指南

2026-04-04 09:31:27作者:管翌锬

问题-场景-价值:为什么执行计划如此重要?

在数据密集型业务中,查询性能直接决定了系统响应速度和用户体验。想象以下三个典型场景:

场景1:报表查询超时
某电商平台的日销报表查询在早高峰持续5分钟未返回结果,导致运营团队无法及时调整营销策略。经排查发现,查询未命中分区且未使用索引,导致全表扫描8亿行数据。

场景2:实时分析延迟
某金融风控系统的实时交易监控出现30秒延迟,经查是JOIN算子选择错误,小表驱动大表导致内存溢出。

场景3:批量任务阻塞
ETL任务在夜间执行时,因聚合算子未启用部分聚合,导致计算节点负载过高,影响其他业务正常运行。

这些问题的共同根源在于执行计划(查询优化器生成的执行方案) 不合理。Apache Doris提供的执行计划分析工具,正是解决这类问题的关键。通过EXPLAIN命令,我们能直观看到查询的内部执行流程,精准定位性能瓶颈。

诊断流程:从现象到本质的分析方法

🔍 现象识别:如何发现执行计划问题?

执行计划异常通常表现为:

  • 查询耗时远超预期
  • CPU/内存使用率异常升高
  • 网络IO或磁盘IO突发峰值

快速诊断命令

-- 基础执行计划查看
EXPLAIN SELECT count(*) FROM sales WHERE dt = '2023-10-01';

-- 查看详细执行步骤(含Nereids优化器)
EXPLAIN FORMAT=verbose SELECT /*+ SET_VAR(enable_nereids_planner=true) */ 
  user_id, sum(amount) 
FROM orders 
GROUP BY user_id;

-- 对比不同优化器执行计划
EXPLAIN FORMAT=graph SELECT /*+ SET_VAR(enable_nereids_planner=false) */ * FROM t1 JOIN t2 ON t1.id = t2.id;

📊 原理分析:执行计划的核心组成

执行计划由算子(Operator) 按数据流向组成有向无环图,每个算子承担特定数据处理任务。关键算子包括:

1. SCAN算子
负责从存储引擎读取数据,常见类型:

  • OLAP_TABLE_SCAN:读取Doris原生表
  • MYSQL_SCAN:读取MySQL外部表
  • HIVE_SCAN:读取Hive外部表

示例PROPERTIES信息:

table: sales, partitions: [p202310], buckets: [1-8], predicates: dt = '2023-10-01'

2. JOIN算子
实现表连接操作,Doris支持:

  • HASH_JOIN:适用于大表连接(默认)
  • MERGE_JOIN:适用于已排序数据
  • NESTED_LOOP_JOIN:适用于小表连接

3. AGGREGATE算子
处理聚合计算,分为:

  • PARTIAL_AGGREGATE:分片预聚合
  • FINAL_AGGREGATE:全局聚合

4. EXCHANGE算子
实现节点间数据传输,类型包括:

  • HASH_EXCHANGE:按哈希重分布
  • BROADCAST_EXCHANGE:广播小表数据
  • GATHER_EXCHANGE:结果收集

💡 新旧方案对比:Nereids vs Legacy Planner

Doris提供两代优化器,核心差异如下:

特性 Legacy Planner Nereids Planner
优化框架 启发式规则 Cascades基于代价
算子选择 有限固定策略 动态生成最优算子
代价估算 简单统计 精确代价模型
支持功能 基础查询 复杂子查询/CTE/MV

切换方法

-- 会话级别启用Nereids
SET enable_nereids_planner = true;

-- 语句级别指定优化器
SELECT /*+ SET_VAR(enable_nereids_planner=true) */ * FROM table;

Nereids优化器的核心实现可见源码:fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java,其通过Cascades框架实现了灵活的计划空间搜索。

优化策略:从诊断到落地的实施路径

案例1:全表扫描优化

问题描述:某用户行为分析查询耗时20秒,执行计划显示:

| 4  | SCAN | OLAP_TABLE | 1000000 | 40000000 | table: user_log, partitions: [], buckets: [] |

分析过程

  • partitions和buckets为空,确认发生全表扫描
  • 表按dt分区,但查询未指定分区条件

优化方案

  1. 添加分区过滤:WHERE dt = '2023-10-01'
  2. 对高频过滤字段创建 Bloom Filter 索引:
ALTER TABLE user_log ADD INDEX idx_uid (user_id) USING bloom_filter WITH (fpp=0.05);

优化结果:扫描数据量从1亿行降至500万行,耗时2.3秒。

案例2:JOIN顺序优化

问题描述:三表JOIN查询内存溢出,执行计划片段:

| 0  | HASH_JOIN |  | 1000 | ... | join_type: INNER, condition: a.id = b.user_id |
| 1  |  HASH_JOIN |  | 10000 | ... | join_type: INNER, condition: b.order_id = c.id |
| 2  |   SCAN | TABLE_A | 100000 | ... |  |
| 3  |   SCAN | TABLE_B | 10000 | ... |  |
| 4  |  SCAN | TABLE_C | 1000 | ... |  |

分析过程

  • TABLE_A(10万行)先与TABLE_B(1万行)JOIN,产生大量中间结果
  • 最优策略应先JOIN小表TABLE_B和TABLE_C(1千行)

优化方案: 使用JOIN_ORDER hint强制调整连接顺序:

EXPLAIN SELECT /*+ JOIN_ORDER(TABLE_B, TABLE_C, TABLE_A) */ ...

优化结果:中间结果集从100万行降至1万行,内存使用减少90%。

执行计划健康度评分表

评估项 健康指标 风险指标 权重
扫描效率 仅扫描必要分区/分桶 全表扫描 30%
连接策略 小表广播/大表哈希 大表广播 25%
聚合方式 存在PARTIAL_AGGREGATE 仅FINAL_AGGREGATE 20%
数据分布 均匀HASH分布 数据倾斜 > 3:1 15%
算子顺序 过滤先于JOIN/聚合 大表先JOIN 10%

评分标准:80分以上为优秀,60-80分为良好,低于60分需优化。

最佳实践清单

操作步骤 关键命令 应用场景
1. 启用Nereids SET enable_nereids_planner=true 复杂查询优化
2. 查看图形化计划 EXPLAIN FORMAT=graph SELECT ... 分析算子关系
3. 强制部分聚合 /*+ PARTIAL_AGGREGATION() */ 大数据量聚合
4. 调整JOIN顺序 /*+ JOIN_ORDER(t1, t2, t3) */ 多表连接优化
5. 查看分区修剪 EXPLAIN ANALYZE SELECT ... 验证分区过滤效果

通过这套系统化的诊断和优化方法,大多数查询性能问题都能得到有效解决。记住,优秀的查询性能不是偶然的,而是通过对执行计划的深度理解和精细调优实现的。Apache Doris的执行计划工具,正是你达成这一目标的关键助手。

登录后查看全文
热门项目推荐
相关项目推荐