前言

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_queryqueryloadstream_loadAudit 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 PLANMEMO 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(快速定位整体结构)

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

  • 适用阶段:初步分析,排除结构性错误(如错误的 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)
  • 分析:
    • 确认查询包含 HashJoinHashAggregate 两个关键操作符。
    • 检查 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 ProfileExecution Profile 以及 PipelineTask 的执行时间含义:

1、Profile 文件结构

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

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

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

    • 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 TimeSchedule Time 等):

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

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

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

2、Merged Profile操作符聚合指标

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

指标名称 指标含义 说明
BlocksProduced 产生的 Data Block 数量  
CloseTime Operatorclose 阶段的耗时  
ExecTime Operator 在各个阶段执行的总耗时  
InitTime OperatorInit 阶段的耗时  
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 ProfileExecution Profile 展示单个并发实例的详细指标,适用于定位数据倾斜或单实例性能问题。以 id=4 的这个 exchange operator 为例:

示例:EXCHANGE_OPERATORExecution 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 可能拆分为 ScanPipelineProjectPipeline
  3. Task 层:
    • 每个 Pipeline 拆分为多个 PipelineTask(基于并行度 pipeline_dop)。
    • 例如:ScanPipeline 拆分为 4 个 Task,每个 Task 处理一个数据分片。
关键指标 解释 说明 使用
ExecuteTime 实际执行时间 PipelineTask 中所有 OperatorExecTime 之和 反映任务实际处理数据的耗时。
WaitWorkerTime 等待 Worker 时间 task 处于 runnable 状态时,他要等待一个空闲 worker 来执行,这个耗时主要取决于集群负载 等待可用工作线程的时间,反映了系统资源竞争程度。
WaitForDependencyTime 等待依赖时间 Task 等待上游 Operator 输出数据或系统资源(如 BufferRPC)的时间 依赖等待时间远高于实际执行时间,说明 上游数据生产是瓶颈

案例-现象

  • 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