前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、调优概述

查询性能调优是一个系统工程,需要从多层次、多维度对数据库系统进行调优。以下是调优流程和方法论概述:

  1. 首先,业务人员和数据库管理员(DBA)需要对所使用的数据库系统有全面的了解,包括业务系统使用的硬件、集群的规模、使用的数据库软件版本,以及具体软件版本所提供的特性等。
  2. 其次,一个好用的性能诊断工具是定位性能问题的必要前提。只有高效快速地定位到问题 SQL 或者慢 SQL,才能进行后续的具体性能调优流程。
  3. 在进入性能调优环节之后,一些常用的性能分析工具是必不可少的。这其中包括当前运行数据库系统自带的特有工具,以及操作系统层面的通用工具。
  4. 有了上述工具之后,使用特有工具可以获取 SQL 运行在当前数据库系统上的详细信息,帮助定位性能瓶颈,同时,通用工具也可以作为辅助分析手段帮助定位问题。

1、诊断工具

高效好用的性能诊断工具对于数据库系统的调优至关重要,因为这取决于是否能快速定位到存在性能问题的业务 SQL,继而快速定位和解决性能瓶颈,保证数据库系统服务的 SLA 。

当前,Doris 系统默认将执行时间超过 5 秒的 SQL 认定为慢 SQL,此阈值可通过 config.qe_slow_log_ms 进行配置。目前 Doris 提供了以下三种诊断渠道,能够帮助快速定位存在性能问题的慢 SQL,分别如下:

1)Doris Manager 日志

Doris Manager 的日志模块提供了慢 SQL 筛选功能。用户可以通过选择特定 FE 节点上的 fe.audit.log 来查看慢 SQL。只需在搜索框中输入“slow_query”,即可在页面上展示当前系统的历史慢 SQL 信息,如下图所示:

image-20250627144906752

2)Audit Log

当前 Doris FE 提供了四种类型的 Audit Log,包括 slow_query、query、load 和 stream_load。Audit Log 除了在安装部署 Manager 服务的集群上通过日志页面访问获取之外,也可以直接访问 FE 所在节点的 fe/log/fe.audit.log 文件获取信息。

通过直查 fe.audit.log 中的 slow_query 标签,可以快速筛选出执行缓慢的查询 SQL。

通过 fe.audit.log 获取的慢 SQL,使用者可以方便地获取执行时间、扫描行数、返回行数、SQL 语句等详细信息,为进一步重现和定位性能问题奠定了基础。

3)audit_log 系统表

audit_log 系统表:Doris 2.1 以后的版本在 __internal_schema 数据库下提供了 audit_log 系统表,供用户查看 SQL 运行的情况。使用前需要打开全局配置 set global enable_audit_plugin=true;(此开关默认关闭)

ysql> use __internal_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-----------------------------+
| Tables_in___internal_schema |
+-----------------------------+
| audit_log                   |
| column_statistics           |
| histogram_statistics        |
| partition_statistics        |
+-----------------------------+
4 rows in set (0.00 sec)

mysql> desc audit_log;
+-------------------+--------------+------+-------+---------+-------+
| Field             | Type         | Null | Key   | Default | Extra |
+-------------------+--------------+------+-------+---------+-------+
| query_id          | varchar(48)  | Yes  | true  | NULL    |       |
| time              | datetime     | Yes  | true  | NULL    |       |
| client_ip         | varchar(128) | Yes  | true  | NULL    |       |
| user              | varchar(128) | Yes  | false | NULL    | NONE  |
| catalog           | varchar(128) | Yes  | false | NULL    | NONE  |
| db                | varchar(128) | Yes  | false | NULL    | NONE  |
| state             | varchar(128) | Yes  | false | NULL    | NONE  |
| error_code        | int          | Yes  | false | NULL    | NONE  |
| error_message     | text         | Yes  | false | NULL    | NONE  |
| query_time        | bigint       | Yes  | false | NULL    | NONE  |
| scan_bytes        | bigint       | Yes  | false | NULL    | NONE  |
| scan_rows         | bigint       | Yes  | false | NULL    | NONE  |
| return_rows       | bigint       | Yes  | false | NULL    | NONE  |
| stmt_id           | bigint       | Yes  | false | NULL    | NONE  |
| is_query          | tinyint      | Yes  | false | NULL    | NONE  |
| frontend_ip       | varchar(128) | Yes  | false | NULL    | NONE  |
| cpu_time_ms       | bigint       | Yes  | false | NULL    | NONE  |
| sql_hash          | varchar(128) | Yes  | false | NULL    | NONE  |
| sql_digest        | varchar(128) | Yes  | false | NULL    | NONE  |
| peak_memory_bytes | bigint       | Yes  | false | NULL    | NONE  |
| stmt              | text         | Yes  | false | NULL    | NONE  |
+-------------------+--------------+------+-------+---------+-------+

2、分析工具

上节[诊断工具]已经帮助业务和运维人员定位到具体的慢 SQL,本章节开始介绍如何对慢 SQL 的性能瓶颈进行分析,以确定具体慢在 SQL 执行的哪个环节。

一条 SQL 的执行过程大致可以分为计划生成和计划执行两个阶段,前一部分负责生成执行计划,后一部分负责具体计划的执行。

  • 性能瓶颈

    • 计划生成问题:这两个部分出现问题都可能导致性能瓶颈的发生。比如生成了差计划,那么即使再优秀的执行器也不可能获得很好的性能。

    • 执行手段问题:同样一个正确的计划,如果相应的执行手段不合适,也容易产生性能瓶颈。
    • 技术设施问题:此外,执行器的性能和当前运行的硬件和系统架构有紧密的关系,一些基础设施的缺陷或者配置不正确也会导致性能问题。

上述三类问题都需要良好的分析工具的支持。基于此,Doris 系统提供了两个性能分析工具来分别分析计划以及执行的性能瓶颈。另外系统级别也提供了相应的性能检测工具,辅助定位性能瓶颈。下面分别就这三个方面进行介绍:

二、计划生成: Explain

执行计划是对一条 SQL 具体的执行方式和执行过程的描述。例如,对于一个两表连接的 SQL,执行计划会展示这两张表的访问方式信息、连接方式信息,以及连接的顺序等。Doris 提供了 Explain 工具,可以方便的展示一个 SQL 的执行计划的详细信息。通过对 Explain 输出的计划进行分析,可以帮助使用者快速定位计划层面的瓶颈,从而针对不同的情况进行计划层面的调优。

调优原则:

  1. 从抽象到具体:先通过 SHAPE PLAN 确认整体结构,再逐步深入细节。

  2. 从逻辑到物理:先用 ALL PLAN 和 MEMO PLAN 分析逻辑优化,再用 VERBOSE 调试物理执行。

  3. 从成本到资源:通过 ALL PLAN 比较计划成本,再通过 VERBOSE 检查资源分配(如内存、线程)。

顺序 工具 核心目标 典型问题定位
1 EXPLAIN SHAPE PLAN 快速理解查询逻辑架构,快速定位整体结构 错误的 Join 顺序、缺失的聚合操作符
2 EXPLAIN ALL PLAN 比较备选方案,验证优化器选择是否合理 过滤条件下推失败、备选计划成本异常
3 EXPLAIN MEMO PLAN 调试优化器规则 预期规则未触发、规则应用顺序错误
4 EXPLAIN VERBOSE 检查操作符物理执行参数,调试底层细节 内存不足、线程竞争、Join 策略不合理

1、工具介绍

1)EXPLAIN SHAPE PLAN(快速定位整体结构)

  • 目的:快速理解查询的逻辑架构,确认关键操作符(如 JOIN、AGGREGATE)是否存在明显问题。

  • 适用阶段:初步分析,排除结构性错误(如错误的 Join 顺序)。

2)EXPLAIN ALL PLAN(比较备选方案)

  • 目的:验证优化器选择的计划是否合理,分析备选方案被放弃的原因(如成本过高)。
  • 适用阶段:确认优化器行为是否符合预期,发现潜在优化机会。

3)EXPLAIN MEMO PLAN(调试优化器规则)

  • 目的:深入分析优化器内部的规则匹配和转换过程,定位规则未生效的原因。
  • 适用阶段:优化器行为异常时(如预期的规则未触发)。

4)EXPLAIN VERBOSE(调试底层细节)

  • 目的:检查操作符的物理执行参数(如内存分配、线程模型),解决底层性能问题。
  • 适用阶段:针对具体操作符(如 HashJoin)进行深度调优。

2、调优举例

1)案例1

SELECT d.dept_name, AVG(e.salary)
FROM departments d
         JOIN employees e ON d.id = e.dept_id
WHERE e.hire_date > '2020-01-01'
GROUP BY d.dept_name;

a、使用 EXPLAIN SHAPE PLAN 快速定位结构

EXPLAIN SHAPE PLAN
SELECT d.dept_name, AVG(e.salary)
FROM departments d
         JOIN employees e ON d.id = e.dept_id
WHERE e.hire_date > '2020-01-01'
GROUP BY d.dept_name;


ROOT: HashAggregate
  |-- HashJoin(condition=[id = dept_id])
  |   |-- Scan(departments)
  |   +-- Scan(employees)
  +-- GroupBy(dept_name)
  • 分析:
    • 确认查询包含 HashJoin 和 HashAggregate 两个关键操作符。
    • 检查 Scan(employees) 是否在 HashJoin 之前,可能存在过滤条件下推问题。

b、使用 EXPLAIN ALL PLAN 比较备选方案

EXPLAIN ALL PLAN
SELECT d.dept_name, AVG(e.salary)
FROM departments d
         JOIN employees e ON d.id = e.dept_id
WHERE e.hire_date > '2020-01-01'
GROUP BY d.dept_name;



Plan 1 (cost=1000):  # 优化器选择的计划
  - HashJoin: d.id = e.dept_id
  - Scan(departments): full scan
  - Scan(employees): filtered by hire_date > '2020-01-01'

Plan 2 (cost=2000):  # 被放弃的备选方案
  - HashJoin: d.id = e.dept_id
  - Scan(departments): full scan
  - Scan(employees): full scan + Filter hire_date > '2020-01-01'
  • 分析:
    • 优化器正确选择了 Plan 1,将过滤条件 hire_date > '2020-01-01' 下推到 employees 表的扫描阶段。
    • 若输出显示 Plan 2 被选中,则说明过滤条件下推失败,需进一步调试。

c、使用 EXPLAIN MEMO PLAN 调试优化器规则

EXPLAIN MEMO PLAN
SELECT d.dept_name, AVG(e.salary)
FROM departments d
         JOIN employees e ON d.id = e.dept_id
WHERE e.hire_date > '2020-01-01'
GROUP BY d.dept_name;


Memo Group 0:
  - LogicalAggregate(groupBy=[dept_name], avg=[salary])
  - LogicalJoin(condition=[id=dept_id])
    - LogicalScan(departments)
    - LogicalScan(employees)

Applied Rules:
  - PushFilterThroughJoin: Success (hire_date > '2020-01-01' pushed to employees)
  - MergeAdjacentAggregates: Failed (no adjacent aggregates)
  • 分析:
    • 确认规则 PushFilterThroughJoin 成功应用,过滤条件已下推。
    • 若规则未生效,需检查表统计信息或优化器配置。

d、使用 EXPLAIN VERBOSE 调试底层细节

EXPLAIN VERBOSE
    SELECT d.dept_name, AVG(e.salary) 
FROM departments d JOIN employees e ON d.id = e.dept_id 
WHERE e.hire_date > '2020-01-01' 
GROUP BY d.dept_name;



- HashJoin: d.id = e.dept_id (BuildSide=departments, ProbeSide=employees)
- OlapScanNode(departments): tablets=10, memory_usage = 128MB
- OlapScanNode(employees): tablets=100, filtered by hire_date > '2020-01-01', memory_usage=512MB
  • 分析:
    • 确认 HashJoin 的构建侧(BuildSide)和探测侧(ProbeSide)分配合理。
    • 检查 employees 表的扫描内存(memory_usage)是否足够,避免溢出到磁盘。

三、计划执行:Profile

当 SQL 具体执行时,如何了解每个具体的执行分别耗费多少时间,比如 build 耗费多少时间,probe 耗费多少时间,profile 工具提供了详细的执行信息供性能分析和调优使用。下面部分先整体介绍 Profile 的文件结构,然后分别介绍 Merged Profile,Execution Profile 以及 PipelineTask 的执行时间含义:

1、Profile 文件结构

Profile 文件中包含几个主要的部分:

  1. 查询基本信息:包括 ID,时间,数据库等。

    • Query ID: 123456789
      Start Time: 2025-07-08 12:00:00
      Database: sales_db
      
  2. SQL 语句以及执行计划:展示原始 SQL 及优化后的物理执行计划(如 HashJoin、Aggregate 等操作符的树状结构)

    • SQL: SELECT d.dept_name, AVG(e.salary) 
              FROM departments d JOIN employees e ON d.id = e.dept_id GROUP BY d.dept_name;
      Plan:
        ROOT: HashAggregate
          |-- HashJoin(condition=[ id = dept_id])
          |   |-- Scan(departments)
          |   +-- Scan(employees)
          +-- GroupBy(dept_name)
      
  3. FE 的耗时(Plan Time,Schedule Time 等):

    • Plan Time 计划生成时间,数据库优化器为查询生成执行计划所花费的时间

    • Schedule Time 调度执行时间,从执行计划开始执行到返回结果的总时间

    • FE Timing:
        Plan Time: 15ms
        Schedule Time: 5ms
      
  4. BE 在执行过程中各个 operator 的执行耗时(包括 Merged Profile 和 Execution Profile)

2、Merged Profile:操作符聚合指标)

Merged Profile ,对每个 Operator 操作符的并发执行实例进行指标聚合,提供以下核心字段:

指标名称 指标含义 说明
BlocksProduced 产生的 Data Block 数量  
CloseTime Operator 在 close 阶段的耗时  
ExecTime Operator 在各个阶段执行的总耗时  
InitTime Operator 在 Init 阶段的耗时  
MemoryUsage Operator 在执行阶段的内存用量 如果内存使用峰值较高,需关注是否触发磁盘溢出(Spill)。
OpenTime Operator 在 Open 阶段的耗时  
ProjectionTime Operator 在做 projection 的耗时  
RowsProduced Operator 返回的行数  
WaitForDependencyTime Operator 等待自身执行的条件依赖的时间(如 EXCHANGE_OPERATOR 等待 RPC 数据)。 该操作符耗时主要在等待数据,可能存在网络瓶颈或上游算子性能问题。
EXCHANGE_OPERATOR  (id=4):
    -  BlocksProduced:  sum  0,  avg  0,  max  0,  min  0
    -  CloseTime:  avg  34.133us,  max  38.287us,  min  29.979us
    -  ExecTime:  avg  700.357us,  max  706.351us,  min  694.364us
    -  InitTime:  avg  648.104us,  max  648.604us,  min  647.605us
    -  MemoryUsage:  sum  ,  avg  ,  max  ,  min  
    -  PeakMemoryUsage:  sum  0.00  ,  avg  0.00  ,  max  0.00  ,  min  0.00  
    -  OpenTime:  avg  4.541us,  max  5.943us,  min  3.139us
    -  ProjectionTime:  avg  0ns,  max  0ns,  min  0ns
    -  RowsProduced:  sum  0,  avg  0,  max  0,  min  0
    -  WaitForDependencyTime:  avg  0ns,  max  0ns,  min  0ns
    -  WaitForData0:  avg  9.434ms,  max  9.476ms,  min  9.391ms

3、Execution Profile:单并发实例详情

区别于 Merged Profile,Execution Profile 展示单个并发实例的详细指标,适用于定位数据倾斜或单实例性能问题。以 id=4 的这个 exchange operator 为例:

示例:EXCHANGE_OPERATOR 的 Execution Profile

分析:

  • 该实例处理了 50 万行数据,耗时 706μs,无显著等待时间。
  • 若其他实例的 ExecTime 显著高于此值,可能存在数据倾斜。
EXCHANGE_OPERATOR  (id=4):(ExecTime:  706.351us)
    -  BlocksProduced:  0
    -  CloseTime:  38.287us
    -  DataArrivalWaitTime:  0ns
    -  DecompressBytes:  0.00  
    -  DecompressTime:  0ns
    -  DeserializeRowBatchTimer:  0ns
    -  ExecTime:  706.351us
    -  FirstBatchArrivalWaitTime:  0ns
    -  InitTime:  647.605us
    -  LocalBytesReceived:  512MB  
    -  MemoryUsage:  
    -  PeakMemoryUsage:  0.00  
    -  OpenTime:  5.943us
    -  ProjectionTime:  0ns
    -  RemoteBytesReceived:  0.00  
    -  RowsProduced:  500000
    -  SendersBlockedTotalTimer(*):  0ns
    -  WaitForDependencyTime:  0ns
    -  WaitForData0:  9.476ms

4、PipelineTask :并行任务调度

在 Doris 中,执行计划会拆分为多个 Pipeline Task,1个 PipelineTask 由多个 operator 组成。

  1. Fragment 层:
    • FE 将查询计划拆分为多个 Fragment(每个 Fragment 是一个子计划,可能跨节点)。
    • 例如:Map Fragment(数据扫描)和 Reduce Fragment(聚合/Join)。
  2. Pipeline 层:
    • 每个 Fragment进一步拆分为多个 Pipeline(基于数据依赖关系)。
    • 例如:Map Fragment 可能拆分为 ScanPipeline 和 ProjectPipeline。
  3. Task 层:
    • 每个 Pipeline 拆分为多个 PipelineTask(基于并行度 pipeline_dop)。
    • 例如:ScanPipeline 拆分为 4 个 Task,每个 Task 处理一个数据分片。
关键指标 解释 说明 使用
ExecuteTime: 实际执行时间 PipelineTask 中所有 Operator 的 ExecTime 之和 反映任务实际处理数据的耗时。
WaitWorkerTime 等待 Worker 时间 task 处于 runnable 状态时,他要等待一个空闲 worker 来执行,这个耗时主要取决于集群负载 等待可用工作线程的时间,反映了系统资源竞争程度。
WaitForDependencyTime 等待依赖时间 Task 等待上游 Operator 输出数据或系统资源(如 Buffer、RPC)的时间 依赖等待时间远高于实际执行时间,说明 上游数据生产是瓶颈。

案例-现象

  • PipelineTask 总耗时 15ms,其中:
    • ExecuteTime: 3ms
    • WaitWorkerTime: 50us
    • WaitForDependencyTime: 12ms(主要由 EXCHANGE_OPERATOR 的 RPC 等待导致)

根因分析

  1. 上游数据倾斜:某些 BE 节点处理慢,导致数据分片延迟。
  2. 网络瓶颈:跨节点数据交换(Shuffle)时网络拥塞。
  3. 资源不足:上游 Operator 的并发度(parallel_fragment_exec_instance_num)设置过低。

四、调优流程

1、使用性能诊断工具进行慢查询定位

针对运行在 Doris 上的业务系统,使用上述性能 诊断工具 进行慢 SQL 的定位。

  • 如果已经安装了 Doris Manager,推荐使用 Manager 日志页面,方便的进行可视化的慢查询定位。

  • 如果没有安装 Manager,可以直查 FE 节点上的 fe.audit.log 或者 audit_log 系统表来获取慢 SQL 列表,按优先级进行调优。

2、Schema 设计与调优

定位到具体的慢 SQL 之后,优先需要对业务 Schema 设计进行检查与调优,排除因为 Schema 设计不合理导致的性能问题。

Schema 设计调优基本可分为三个方面:

  • 表级别 Schema 设计调优:如分区分桶个数和字段调优;
  • 索引的设计和调优;
  • 特定优化手段的使用,如使用 Colocate Group 优化 Join 等。主要目的是排除因为 Schema 设计不合理或者没有充分利用 Doris 现有优化能力导致的性能问题。

3、计划调优

检查和优化完业务 Schema 后,将进入调优的主体工作,即计划调优与执行调优。如上所述,在性能调优工具中,这个阶段的主要工作是充分利用 Doris 所提供的各种层级的 Explain 工具,对慢 SQL 的执行计划进行系统分析,以找到关键优化点进行针对性优化。

4、执行调优

进入执行调优阶段后,需要根据 SQL 的实际运行情况,一方面验证计划调优的效果,另外一方面在现有计划的前提下,继续分析执行侧的瓶颈点,定位哪个执行阶段慢,或者其他普遍性的原因,如并行度不优等。

ContactAuthor