基于湖库一体架构,统一管理结构化、半结构化与非结构化等多模态数据,一个系统承载事务处理、实时分析与 AI 工作负载。
并行执行问题诊断
更新时间:2026-04-07 20:57:20
诊断并行执行问题,可以从两个大的方面入手。首先从系统整体上判断,如确认网络、磁盘 IO、CPU 是否被打满;然后从具体 SQL 着手,找到问题 SQL 在哪里,它的内部状态如何。
系统诊断
在业务比较繁忙的系统中出现性能问题时,首先需要在系统层面做初步诊断。一般有两种途径:
- OCP(OceanBase Cloud Platform),支持可视化观测系统性能。
- tsar 等命令行系统工具,支持查询网络、磁盘、CPU 等的历史监控数据。
tsar 是一个系统监控和性能分析工具,它可以提供关于 CPU、磁盘、网络等方面的详细信息。以下是 tsar 命令的几个常见用法:
查看 CPU 使用率:
tsar --cpu
查看磁盘 IO 使用情况:
tsar --io
查看网络传输速率:
tsar --traffic
除了以上示例外,tsar 还支持其他选项和参数,例如通过参数 -d 2 可以查出两天前到现在的数据,-d YYYY-MM-DD 可以查看指定日期 YYYY-MM-DD 的数据,-i 1 表示以每次 1 分钟作为采集显示。
查看过去2天的 cpu 使用情况:
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),遇到这种情况,请参考 并行执行分类 调大 undo_retention 值即可解决。
-- 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 视图查看到。
-- 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,每个算子使用了多少个线程来执行:
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,当前正在执行哪些算子,使用了多少线程,已经吐出了多少行:
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,每个算子处理了多少行数据,吐出了多少行数据:
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 问题:
确认 PDML 正在执行。
obclient> SELECT * FROM .all_virtual_px_worker.stat;确认 PDML 的总执行次数(threds 字段),如果远大于并行度,说明有重试。
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;从 processlist 虚拟表上可以进一步确认有重试,retry_cnt 字段大于 0。
-- 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;修改 undo_retention,使其值大于单个 PDML 执行的最大时间,单位为“秒”。
obclient> SHOW VARIABLES LIKE "%undo%" +---------------+-------+ | Variable_name | Value | +---------------+-------+ | undo_retention| 1800 | +---------------+-------+ 1 row in set