---
title: "并行执行问题诊断 - OceanBase 数据库 V4.3.5 | OceanBase 文档中心"
description: 并行执行问题诊断 诊断并行执行问题，可以从两个大的方面入手。首先从系统整体上判断，如确认网络、磁盘 IO、CPU 是否被打满；然后从具体 SQL 着手，找到问题 SQL 在哪里，它的内部状态如何。 系统诊断 在业务比较繁忙的系统中出现性能问题时，首先需要在系统层面做初步诊断。一般有两种途径： OCP（OceanBas…
image: https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*OSPzQ6GUQF4AAAAAQHAAAAgAeiGDAQ/original
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*P8CuR4UJ_FkAAAAAAAAAAAAADiGDAQ/original) OceanBase 数据库分布式版 - V 4.3.5 LTS

# 并行执行问题诊断

更新时间：2026-04-07 20:57:20

[编辑](https://github.com/oceanbase/oceanbase-doc/edit/V4.3.5/zh-CN/700.reference/1000.performance-tuning-guide/500.sql-optimization/350.parallel-execution/500.diagnostics-parallel-execution.md)  

诊断并行执行问题，可以从两个大的方面入手。首先从系统整体上判断，如确认网络、磁盘 IO、CPU 是否被打满；然后从具体 SQL 着手，找到问题 SQL 在哪里，它的内部状态如何。

## 系统诊断

在业务比较繁忙的系统中出现性能问题时，首先需要在系统层面做初步诊断。一般有两种途径：

- OCP（OceanBase Cloud Platform），支持可视化观测系统性能。
 - tsar 等命令行系统工具，支持查询网络、磁盘、CPU 等的历史监控数据。

tsar 是一个系统监控和性能分析工具，它可以提供关于 CPU、磁盘、网络等方面的详细信息。以下是 tsar 命令的几个常见用法：

查看 CPU 使用率：

```sql
tsar --cpu

```

查看磁盘 IO 使用情况：

```sql
tsar --io

```

查看网络传输速率：

```sql
tsar --traffic

```

除了以上示例外，`tsar` 还支持其他选项和参数，例如通过参数 `-d 2` 可以查出两天前到现在的数据，`-d YYYY-MM-DD` 可以查看指定日期 `YYYY-MM-DD` 的数据，`-i 1` 表示以每次 1 分钟作为采集显示。

查看过去2天的 cpu 使用情况：

```sql
tsar -n 2 -i 1 --cpu

```

如果出现磁盘或网络打爆，则优先从硬件容量过小或并发压力过大角度解决问题。

## SQL 诊断

当遇到并行执行问题时，可以从 SQL 层面、并行执行线程层面、算子层面逐层检查。

### 确认 SQL 还在执行

确认 SQL 在正常执行：关注 TIME 字段，每次查询 `GV$OB_PROCESSLIST` 视图 TIME 字段都在增长，并且 STATE 为 ACTIVE，说明 Query 还在执行。

确认 SQL 是否在反复重试：如果 SQL 是因为反复重试导致没有返回结果，RETRY_CNT、RETRY_INFO 字段会有相关信息。其中 RETRY_CNT 是表示重试了多少次了，RETRY_INFO 是最后一次重试的原因。 没有重试发生的时候，RETRY_CNT 为 0。TOTAL_TIME 字段表示包含每次重试在内的累计执行时间。

如果发现 SQL 在反复重试，则根据 RETRY_INFO 中给出的错误码判断是否需要干预。V4.1 之前，最常见的一个错误是 -4138（OB_SNAPSHOT_DISCARDED），遇到这种情况，请参考 [并行执行分类](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002013744) 调大 undo_retention 值即可解决。

```sql
-- MySQL 模式
SELECT
  TENANT,INFO,TRACE_ID,STATE,TIME,TOTAL_TIME,RETRY_CNT,RETRY_INFO
FROM
  oceanbase.GV$OB_PROCESSLIST;

-- Oracle 模式
SELECT
  TENANT,INFO,TRACE_ID,STATE,TIME,TOTAL_TIME,RETRY_CNT,RETRY_INFO
FROM
  sys.GV$OB_PROCESSLIST;

```

### 确认 SQL 还在执行并行查询

OBServer 集群中，所有活跃的并行执行线程都可以通过 `GV$OB_PX_WORKER_STAT` 视图查看到。

```sql
-- MySQL 模式
obclient> SELECT * FROM oceanbase.GV$OB_PX_WORKER_STAT;
SESSION_ID: 3221520411
 TENANT_ID: 1002
    SVR_IP: 192.xx.xx.xx
  SVR_PORT: 19510
  TRACE_ID: Y4C360B9E1F4D-0005F9A76E9E66B2-0-0
     QC_ID: 1
    SQC_ID: 0
 WORKER_ID: 0
    DFO_ID: 0
START_TIME: 2023-04-23 17:29:17.372461

-- Oracle 模式
obclient> SELECT * FROM SYS.GV$OB_PX_WORKER_STAT;
SESSION_ID: 3221520410
 TENANT_ID: 1004
    SVR_IP: 192.xx.xx.xx
  SVR_PORT: 19510
  TRACE_ID: Y4C360B9E1F4D-0005F9A76E9E66B1-0-0
     QC_ID: 1
    SQC_ID: 0
 WORKER_ID: 0
    DFO_ID: 0
START_TIME: 2023-04-23 17:29:15.372461

```

结合从 `GV$OB_PROCESSLIST` 拿到的 TRACE_ID，通过这个视图可以看到 SQL 当前正在执行哪些 DFO，执行了多久等信息。

如果这个视图里什么也查不到，但 `GV$OB_PROCESSLIST` 里依然可以看到相应 SQL，可能的原因包括：

- 所有 DFO 均已执行完成，结果集较大，当前正处在向客户端吐数据阶段。
 - 除了最顶层 DFO 外，其余所有 DFO 均已执行完成。

### 确认每个算子的执行状况

通过 oceanbase.GV$SQL_PLAN_MONITOR（MySQL）或 SYS.GV$SQL_PLAN_MONITOR（Oracle）可以查看每个并行工作线程中每个算子的执行状态。从 V4.1 起，`GV$SQL_PLAN_MONITOR` 包含两部分数据：

- 已经执行完成的算子。所谓执行完成是指这个算子已经调用过 close 接口，在当前线程中不再处理任何数据。
 - 正在执行的算子。所谓正在执行是指这个算子还没有调用 close 接口，正在处理数据过程中。读取这部分算子的数据，需要在查询 `GV$SQL_PLAN_MONITOR` 视图的 where 条件中指定 request_id < 0。在使用 request_id < 0 条件查询本视图时，我们也称为访问 “Realtime SQL PLAN MONITOR”。本访问接口未来可能会变化。

V4.1 之前，仅支持查看已经执行完成的算子状态。

`GV$SQL_PLAN_MONITOR` 中有几个重要的域：

- TRACE_ID：它唯一标识了一条 SQL。
 - PLAN_LINE_ID：算子在执行计划中的编号，对应于通过 explain 语句查看到的编号。
 - PLAN_OPERATION：算子名称，如 TABLE SCAN、HASH JOIN。
 - OUTPUT_ROWS：当前算子已经输出的行数。
 - FIRST_CHANGE_TIME：算子吐出首行数据时间。
 - LAST_CHANGE_TIME：算子吐出最后一行数据时间。
 - FIRST_REFRESH_TIME：算子开始监控时间。
 - LAST_REFRESH_TIME：算子结束监控时间。

根据上面几个域，基本就能刻画出一个算子处理数据的主要动作了。以下举例几个场景：

- 查看一个已经执行完成的 SQL，每个算子使用了多少个线程来执行：

```sql
SELECT PLAN_LINE_ID, PLAN_OPERATION, COUNT(*) THREADS
FROM GV$SQL_PLAN_MONITOR
WHERE TRACE_ID = 'YA1E824573385-00053C8A6AB28111-0-0'
GROUP BY PLAN_LINE_ID, PLAN_OPERATION
ORDER BY PLAN_LINE_ID;

+--------------+------------------------+---------+
| PLAN_LINE_ID | PLAN_OPERATION         | THREADS |
+--------------+------------------------+---------+
|            0 | PHY_PX_FIFO_COORD      |       1 |
|            1 | PHY_PX_REDUCE_TRANSMIT |       2 |
|            2 | PHY_GRANULE_ITERATOR   |       2 |
|            3 | PHY_TABLE_SCAN         |       2 |
+--------------+------------------------+---------+
4 rows in set

```

- 查看正在执行的 SQL，当前正在执行哪些算子，使用了多少线程，已经吐出了多少行：

```sql
SELECT PLAN_LINE_ID, CONCAT(LPAD('', PLAN_DEPTH, ' '), PLAN_OPERATION) OPERATOR, COUNT(*) THREADS, SUM(OUTPUT_ROWS) ROWS
FROM GV$SQL_PLAN_MONITOR
WHERE TRACE_ID = 'YA1E824573385-00053C8A6AB28111-0-0' AND REQUEST_ID < 0
GROUP BY PLAN_LINE_ID, PLAN_OPERATION, PLAN_DEPTH
ORDER BY PLAN_LINE_ID;

```

- 查看一个已经执行完成的 SQL，每个算子处理了多少行数据，吐出了多少行数据：

```sql
SELECT PLAN_LINE_ID, CONCAT(LPAD('', PLAN_DEPTH, ' '), PLAN_OPERATION) OPERATOR, SUM(OUTPUT_ROWS) ROWS
FROM GV$SQL_PLAN_MONITOR
WHERE TRACE_ID = 'Y4C360B9E1F4D-0005F9A76E9E6193-0-0'
GROUP BY PLAN_LINE_ID, PLAN_OPERATION, PLAN_DEPTH
ORDER BY PLAN_LINE_ID;
+--------------+-----------------------------------+------+
| PLAN_LINE_ID | OPERATOR                          | ROWS |
+--------------+-----------------------------------+------+
|            0 | PHY_PX_MERGE_SORT_COORD           |    2 |
|            1 |  PHY_PX_REDUCE_TRANSMIT           |    2 |
|            2 |   PHY_SORT                        |    2 |
|            3 |    PHY_HASH_GROUP_BY              |    2 |
|            4 |     PHY_PX_FIFO_RECEIVE           |    2 |
|            5 |      PHY_PX_DIST_TRANSMIT         |    2 |
|            6 |       PHY_HASH_GROUP_BY           |    2 |
|            7 |        PHY_HASH_JOIN              | 2002 |
|            8 |         PHY_HASH_JOIN             | 2002 |
|            9 |          PHY_JOIN_FILTER          | 8192 |
|           10 |           PHY_PX_FIFO_RECEIVE     | 8192 |
|           11 |            PHY_PX_REPART_TRANSMIT | 8192 |
|           12 |             PHY_GRANULE_ITERATOR  | 8192 |
|           13 |              PHY_TABLE_SCAN       | 8192 |
|           14 |          PHY_GRANULE_ITERATOR     | 8192 |
|           15 |           PHY_TABLE_SCAN          | 8192 |
|           16 |         PHY_GRANULE_ITERATOR      | 8192 |
|           17 |          PHY_TABLE_SCAN           | 8192 |
+--------------+-----------------------------------+------+
18 rows in set

```

为了展示美观，上面使用了一个域 PLAN_DEPTH 来做缩进处理，PLAN_DEPTH 表示这个算子在算子树中的深度。

#### 注意

- 尚未调度的 DFO 的算子信息，不会出现在 `GV$SQL_PLAN_MONITOR` 中。
 - 在一个 PL 中如果包含多条 SQL，它们的 TRACE_ID 相同。
 - 如果配置项 `enable_sql_audit` 被设置为 FALSE，则不会记录数据到 `GV$SQL_PLAN_MONITOR`。

### 慢 PDML 诊断

V3.x 版本中，PDML 有一定的特殊性，长时间执行的 PDML 语句会受到 `undo_retention` 变量的限制。当执行时间超过 `undo_retention` 时，可能会发生 SQL 级自动重试，从用户侧看到的效果就是这个 PDML 语句一直不结束。

从以下几个视图可以诊断 PDML 问题：

1. 确认 PDML 正在执行。

   ```sql
   obclient> SELECT * FROM .all_virtual_px_worker.stat;

   ```
 2. 确认 PDML 的总执行次数（threds 字段），如果远大于并行度，说明有重试。

   ```sql
   obclient> select op_id,op,rows,rescan, threads, (close_time - open_time) open_dt, (last_row_eof_time-first_row_time) row_dt, open_time, close_time, first_row_time, last_row_eof_time FROM
   -> (
   -> select plan_line_id op_id, concat(lpad('', plan_depth, ''), plan operation) op, sum(output_rows) rows, sum(STARTS) rescan,min(first_refresh_time) open_time, max(last_refresh_time) close_time,min(first_change_time) first_row_time, max(last_change_time) last_row_eof_time,count(1) threads from oceanbase.gv$sql_plan_monitor where trace_id = 'YB42C0A8051D-000600EFF65FBCFB-0-0' group by plan_line_id, plan_operation order by plan_line_id
   -> )a;

   ```
 3. 从 processlist 虚拟表上可以进一步确认有重试，retry_cnt 字段大于 0。

   ```sql
   -- time: 表示本次重试已经执行了多久
   -- total_time: 从用户发起 sql 到现在，耗时多久
   -- retry_cnt: 已经重试了多少次。如果没有发生重试，则 retry_cnt = 0
   -- retry_info: 最后一次重试是什么错误码触发的
   select time, total_time, trace_id, retry_cnt, retry_info
   from __all_virtual_processlist;

   ```
 4. 修改 undo_retention，使其值大于单个 PDML 执行的最大时间，单位为“秒”。

   ```sql
   obclient> SHOW VARIABLES LIKE "%undo%"
   +---------------+-------+
   | Variable_name | Value |
   +---------------+-------+
   | undo_retention| 1800  |
   +---------------+-------+
   1 row in set

   ```

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
