预计阅读时间: 60 分钟 前置阅读: doc-01, doc-03, doc-04, doc-17 下一次阅读: doc-19(物化视图与查询加速)
1. 调优顺序: 不要从参数开始
慢 SQL 调优最容易犯的错误, 是看到慢就先改并发、内存、RuntimeFilter 等参数。专家调优应该按证据推进:
慢 SQL 识别
→ Schema 是否合理
→ EXPLAIN 静态计划是否合理
→ Profile 运行时瓶颈在哪里
→ 参数、索引、物化视图或 SQL 改写
Apache Doris 官方调优流程也是类似四步: 慢 SQL 识别、Schema 调优、计划调优、执行调优。
参考:
- https://doris.apache.org/docs/4.x/query-acceleration/performance-tuning-overview/tuning-process/
- https://doris.apache.org/docs/4.x/query-acceleration/performance-tuning-intro/
- https://doris.apache.org/docs/4.x/getting-started/before-you-start-the-poc/
2. 先拿到慢 SQL
常见来源:
| 来源 | 适合 |
|---|---|
fe.audit.log | 没有 Doris Manager 时的基础来源 |
audit_log 系统表 | SQL 化筛选慢查询 |
| Doris Manager | 运维界面聚合 |
| 应用侧 trace/log | 关联业务请求 |
筛选维度:
query_time
scan_bytes / scan_rows
return_rows
user
database
workload_group
stmt
优先级不是只看耗时最长:
- 频率高且总耗时高的 SQL 优先。
- 影响核心看板或 API 的 SQL 优先。
- 扫描量明显异常的 SQL 优先。
- 偶发长尾要先判断是不是资源竞争、冷缓存或数据倾斜。
3. EXPLAIN 看静态计划
EXPLAIN 不运行 SQL, 它回答"计划是否看起来合理"。
EXPLAIN
SELECT service_name, count(*)
FROM event_log
WHERE event_time >= '2026-07-27 00:00:00'
AND event_time < '2026-07-28 00:00:00'
AND tenant_id = 1001
GROUP BY service_name;
重点看:
| 位置 | 你要问的问题 |
|---|---|
| Scan Node | 是否命中正确 Partition 和 Tablet |
| predicates | 过滤条件是否下推 |
| cardinality | 行数估计是否离谱 |
| Join | Broadcast、Shuffle、Colocate 是否符合预期 |
| Agg | 是否有本地预聚合 |
| Exchange | 是否发生了昂贵的数据重分布 |
| MV | 是否透明改写到物化视图 |
如果 EXPLAIN 已经显示扫描了大量无关分区, 不要急着看 Profile, 先回到建模和 SQL 条件。
4. Profile 看运行时真相
开启 Profile:
SET enable_profile = true;
SELECT ...;
SHOW PROFILELIST;
SHOW PROFILE WHERE query_id = '...';
Profile 的核心问题:
- 时间花在哪个 Fragment。
- 哪个 Operator 是瓶颈。
- Scan 是慢在 I/O、解压、过滤还是远端读取。
- Join 是慢在 Build、Probe、Shuffle 还是数据倾斜。
- Agg/Sort 是否内存不足或 Spill。
- Exchange 是否网络拥塞或下游反压。
一个简化阅读顺序:
Query Total Time
→ Fragment Instance Time
→ Operator Time
→ Rows / Bytes / Blocks
→ Wait Time / Peak Memory / Spill / Filter Rows
5. Scan 慢怎么判断
Scan 是 OLAP 慢查询最常见瓶颈。先拆成四类:
5.1 扫描范围过大
症状:
- Partition 裁剪很少。
- Tablet 数非常多。
scan_rows远大于返回行数。
处理:
- SQL 加上分区列条件。
- 调整 Partition 粒度。
- 调整 Sort Key。
- 为高频等值列补 Bloom Filter 或 Inverted Index。
- 把重复查询模式做成物化视图。
5.2 数据分布倾斜
症状:
- 某些 Fragment Instance 时间明显长。
- 某些 Tablet 大小明显偏大。
- Hash Bucket 的 Key 有热点。
处理:
SHOW TABLETS FROM table_name;
- 换更均匀的 Bucket Key。
- 对明细日志使用 Random Bucketing。
- 对热点租户拆表或拆更细分区。
5.3 I/O 慢
症状:
- Scan 时间高, CPU 不高。
- 外表查询远慢于内表。
- Data Cache 命中率低。
处理:
- 内表: 检查磁盘、Compaction、Segment 数和 Page Cache。
- 湖仓: 开启 Data Cache, 预热热点数据, 控制小文件。
5.4 过滤没下推
症状:
- Profile 中扫描行很多, 上层 Filter 才过滤。
- 函数包裹列导致谓词无法下推。
反例:
WHERE date(event_time) = '2026-07-27'
更好:
WHERE event_time >= '2026-07-27 00:00:00'
AND event_time < '2026-07-28 00:00:00'
6. Join 慢怎么判断
Join 慢通常来自四件事:
| 问题 | 现象 | 方向 |
|---|---|---|
| Join 顺序不合理 | 大表先 Join 大表 | 统计信息、Leading Hint |
| Shuffle 太重 | Exchange 时间高 | Colocate、Broadcast 小表 |
| Build 侧太大 | Hash Table 内存高 | 过滤前移、聚合前移 |
| 数据倾斜 | 某个 Instance 远慢于其他 | 拆热点 Key、改 Join 策略 |
检查思路:
EXPLAIN SELECT ...
看每个 Join:
- Build Side 是不是小表。
- RuntimeFilter 是否生成并下推。
- 是否出现多层 Exchange。
- 是否能用 Colocate Group 避免 Shuffle。
7. Agg / Sort / TopN 慢怎么判断
聚合慢
原因:
- Group By 基数太高。
- 扫描数据过大。
- 本地预聚合效果差。
- 内存不足导致 Spill。
处理:
- 使用 Aggregate Key 或物化视图预聚合。
- 用 Bitmap/HLL 处理去重。
- 提前过滤。
- 检查 Workload Group 内存。
Sort / TopN 慢
ORDER BY ... LIMIT n 可以触发 TopN 优化, 但不是所有场景都有效。
处理思路:
- 确认 LIMIT 是否足够小。
- 排序列是否能被索引或排序键帮助。
- Profile 中看 TopN 过滤效果。
- 大分页不要用
OFFSET深翻页, 改为游标条件。
参考:
8. 统计信息与 CBO
Nereids 的 CBO 依赖统计信息。统计不准时, 优化器可能低估大表、误判 Join 顺序、选错 Broadcast 或 Shuffle。
常见动作:
ANALYZE TABLE table_name;
SHOW COLUMN STATS table_name;
调优判断:
- 表数据变化很大后重新收集统计。
- 大表 Join 前确认 Join Key 统计存在。
- 过滤列的 NDV 估计明显错误时, 计划可能不稳定。
9. 参数调优应该放在最后
参数是最后一公里, 不是第一选择。
| 参数方向 | 先确认 |
|---|---|
| 并行度 | 是否真的 CPU 不满且 Scan 并行不足 |
| RuntimeFilter 等待 | RF 是否有过滤收益 |
| Spill | 是否允许用磁盘换稳定性 |
| Workload Group | 是否是多租户资源竞争 |
| SQL Block / Policy | 是否需要保护集群稳定 |
专家调优报告应该包含:
1. 慢 SQL 和业务影响
2. 当前表结构与数据量
3. EXPLAIN 计划摘要
4. Profile 瓶颈证据
5. 变更方案和风险
6. 验证 SQL 与前后对比
7. 是否需要回滚
一句话总结:
EXPLAIN 解释"系统准备怎么做", Profile 解释"系统实际怎么慢"; Doris 专家要能把二者连回表设计、执行引擎和资源治理。