OceanBase 诊断调优——(保姆级教程)用 DBMS_XPLAN 快速收集与解读 SQL 执行计划

发布时间:2026/9/26 22:25:11
OceanBase 诊断调优——(保姆级教程)用 DBMS_XPLAN 快速收集与解读 SQL 执行计划 1. 一条慢 SQL 摆在面前先别急着改 SQLOceanBase 里遇到慢查询很多人第一反应是加索引、改 SQL、调并行度但真正高效的路径是先拿到这条 SQL 的执行计划看清楚优化器到底选了什么算子、估行准不准、时间花在哪一步。DBMS_XPLAN 就是干这个的系统包它能展示已执行 SQL 的真实执行计划、优化器全链路追踪日志、SPM 基线计划等是 OceanBase 诊断调优里最常用的工具之一。这篇教程面向已经能连上 OceanBase 集群、想快速定位单条 SQL 性能问题的开发和 DBA。我会从 DBMS_XPLAN 的几个核心子程序讲起然后重点演示怎么用 obdiag 一条命令把 display_cursor 和 opt_trace 两类信息一起收下来再逐字段解读结果最后给出常见报错的排查思路。全程命令可复制跟着敲一遍就能上手。DBMS_XPLAN 支持的子程序不少日常排查用得最多的是这几个DISPLAY_CURSOR 展示已执行查询的计划详情DISPLAY 格式化历史的 EXPLAIN 计划ENABLE_OPT_TRACE / DISABLE_OPT_TRACE 开关当前 Session 的优化器全链路追踪SET_OPT_TRACE_PARAMETER 调整追踪参数DISPLAY_ACTIVE_SESSION_PLAN 看实时计划DISPLAY_SQL_PLAN_BASELINE 看 SPM 基线。手动逐个调用这些包很繁琐obdiag 把这些步骤封装成了一条命令省去记参数的过程。2. 前置准备装好 obdiag 并连上集群obdiag 是 OceanBase 的诊断工具安装方式在 CentOS / RHEL 系上很直接。先加仓库再装包sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://mirrors.aliyun.com/oceanbase/OceanBase.repo sudo yum install -y oceanbase-diagnostic-tool sh /opt/oceanbase-diagnostic-tool/init.sh装完之后配置被诊断集群的连接信息这里用 sys 租户连obdiag config -hxx.xx.xx.xx -urootsys -Pxxxx -p*****配置会写到~/.obdiag/config.yml后续命令默认读这个文件。如果你有多个集群可以用-c指定别的配置文件路径。注意obdiag 连接集群用的账号需要有读取 gv$ob_sql_audit、执行 DBMS_XPLAN 相关包的权限生产环境建议单独建一个诊断账号别直接用 root。装好之后可以先跑obdiag --version确认版本再跑obdiag config看配置是否生效。这一步没问题了后面采集才有基础。3. 可复制配置用 obdiag gather dbms_xplan 一键采集obdiag 采集 DBMS_XPLAN 信息的命令格式是obdiag gather dbms_xplan [options]关键选项如下表选项名是否必选类型默认值说明--trace_id是string空V4.0.0 以下从 gv$sql_audit 取V4.0.0 及以上从 gv$ob_sql_audit 取--scope否stringall可选 opt_trace、display_cursor、all--user是string空待收集 SQL 所在租户的用户名--password是string空对应用户密码--store_dir否string当前路径结果存放目录-c否string~/.obdiag/config.yml配置文件路径--inner_config否string空obdiag 自用配置--config否string空集群配置形如 --config key1value1先建一张测试表模拟一个并行聚合查询create table game ( round int primary key, team varchar(10), score int ) partition by hash(round) partitions 3; insert into game values (1, CN, 4), (2, CN, 5), (3, JP, 3); insert into game values (4, CN, 4), (5, US, 4), (6, JP, 4);执行带并行 hint 的 SQL然后拿 trace_idselect /* parallel(3) */ team, sum(score) total from game group by team; SELECT last_trace_id();last_trace_id()会返回类似YF2A0BA2DA7E-000615B522FD3D35-0-0的字符串把它填进 obdiag 命令obdiag gather dbms_xplan --usertestsys --password***** \ --trace_idYF2A0BA2DA7E-000615B522FD3D35-0-0执行后 obdiag 会依次做几件事开启 opt_trace、设置追踪参数、对目标 SQL 做 explain、关闭 opt_trace然后调用DBMS_XPLAN.DISPLAY_CURSOR拿执行计划。输出大致如下gather_dbms_xplan start ... execute dbms_xplan.enable_opt_trace start ... SET TRANSACTION ISOLATION LEVEL READ COMMITTED call dbms_xplan.enable_opt_trace(); call dbms_xplan.set_opt_trace_parameter(identifierobdiag_m9IRRY, level3); explain select /* parallel(3) */ team, sum(score) total from game group by team call dbms_xplan.disable_opt_trace(); execute dbms_xplan.enable_opt_trace end Gather dbms_xplan.enable_opt_trace: ------------------------------------------------------------------------------------------------------ | Node | Status | Size | Time | PackPath | | xx.xx.xx.xx | Completed | 35.841K | 0 s | ./obdiag_gather_pack_20250625160759/xx_xx_xx_xx_optimizer_trace_f0dfF2_obdiag_m9IRRY.trac | ------------------------------------------------------------------------------------------------------ Gather dbms_xplan.display_cursor: --------------------------------------------------------------------------------------------- | Status | Result Details | Time | | Completed | ./obdiag_gather_pack_20250625160759/obdiag_dbms_xplan_display_cursor.txt | 0.39 s | ---------------------------------------------------------------------------------------------结果统一放在./obdiag_gather_pack_20250625160759/目录下两个文件一个是 opt_trace 日志一个是 display_cursor 结果。如果你想知道 obdiag 内部到底怎么拿的数据可以跑obdiag display-trace trace_id看详细日志。里面能看到它实际执行的 SQLSELECT DBMS_XPLAN.DISPLAY_CURSOR(273900, all, 192.168.1.11, 3882, 1) FROM DUAL这五个参数从左到右是 plan_id、format、svr_ip、svr_port、tenant_id。obdiag 通过 trace_id 自动从集群里把这些参数查出来填进去。这里有个坑如果不指定 svr_ip 和 svr_portDISPLAY_CURSOR可能落到和问题 SQL 不同的节点上拿回来的计划就不是你要的那条。所以手动调用时一定要把节点信息带上或者干脆用 obdiag 让它自动填。4. 验证请求与结果解读opt_trace 和 display_cursor 怎么看采集完成后先看目录结构tree . ├── xx_xx_xx_xx_optimizer_trace_f0dfF2_obdiag_m9IRRY.trac └── obdiag_dbms_xplan_display_cursor.txt4.1 opt_trace 日志定位计划生成阶段的耗时opt_trace 记录的是优化器生成计划的全过程包括 transformer 改写规则、optimizer 的基表路径生成、join order 枚举、top 算子分配等。每个模块结束会打印时间和内存开销select /* PARALLEL(3) */test.game.team,sum(test.game.score) AS total from test.game group by test.game.team ------------------------------------------------------ start prepare mv rewrite info ------------------------------------------------------ table does not have mv, no need to rewrite -- begin 0 iteration ------------------------------------------------------ start transform rule ObTransformMVRewrite ------------------------------------------------------ transform query block: SEL$1 ... transform happened: False SECTION TIME USAGE: 1338 us TOTAL TIME USAGE: 1338 us SECTION MEM USAGE: 240 KB TOTAL MEM USAGE: 240 KBSECTION TIME USAGE 是上一步到当前步的耗时TOTAL TIME USAGE 是从优化开始到当前步的累计耗时内存同理。通过这个可以快速定位哪个改写规则或优化步骤吃掉了大量时间和内存。如果发现某个规则耗时异常可以用 hint 单独关掉它而不是粗暴地加no_rewrite把整个改写流程关掉。4.2 display_cursor 结果读懂执行计划display_cursor 文件里是格式化后的执行计划核心部分长这样|ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)|REAL.ROWS|REAL.TIME(us)|IO TIME(us)|CPU TIME(us)| |0 |PX COORDINATOR | |6 |11 |3 |26263 |13728 |20599 | |1 |└─EXCHANGE OUT DISTR |:EX10001|6 |9 |3 |26263 |0 |42 | |2 | └─HASH GROUP BY | |6 |7 |3 |26263 |0 |176 | |3 | └─EXCHANGE IN DISTR | |6 |6 |3 |26263 |6605 |13293 | |4 | └─EXCHANGE OUT DISTR(HASH)|:EX10000|6 |5 |3 |25206 |0 |13081 | |5 | └─HASH GROUP BY | |6 |3 |3 |25206 |0 |162 | |6 | └─PX BLOCK ITERATOR| |6 |3 |6 |17870 |0 |115 | |7 | └─TABLE FULL SCAN|game |6 |3 |6 |8380 |0 |160 |几个关键列EST.ROWS 是优化器估算行数REAL.ROWS 是实际行数两者偏差大说明统计信息可能有问题CPU TIME 和 IO TIME 帮你判断时间花在计算还是落盘上。计划下面还有几段重要信息。Outputs filters 展示每个算子的输出列和过滤条件比如 TABLE SCAN 那行会标is_index_backfalse说明没回表。Used Hint 显示生效的 hint。Outline Data 是可以用来绑定计划的 outline。Optimization Info 是优化器对每张表的估算细节| game: | | table_rows:6 | | physical_range_rows:6 | | logical_range_rows:6 | | index_back_rows:0 | | output_rows:6 | | table_dop:3 | | dop_method:Global DOP | | avaiable_index_name:[game] | | stats info:[version1970-01-01 08:00:00.000000, is_locked0, is_expired0] | | dynamic sampling level:0 | | estimation method:[DEFAULT, STORAGE] |这里几个字段值得记住table_rows 是原始行数physical_range_rows 是索引上要扫的物理行数index_back_rows 是回表行数table_dop 是表扫描并行度dop_method 说明并行度来源TableDOP / AutoDop / Global DOPstats info 里的 version 是统计信息版本dynamic sampling level 为 0 表示没开动态采样estimation method 为 DEFAULT 说明用的是默认统计信息、估行可能很不准。4.3 快速定位性能差的算子拿到计划后先按 CPU TIME 排序找 topN 算子排除 EXCHANGE IN、EXCHANGE OUT、PX COORDINATOR 这几个协调类算子重点看剩下的TABLE SCAN 如果is_index_backtrue看 index_back_rows 大不大大就考虑优化索引。如果 REAL.ROWS 远高于 EST.ROWS先查统计信息是否过期看 stats version再考虑用/*dynamic_sampling(1)*/提高估行准确度。如果这个算子在 Nested Loop Join 或 SubPlan Filter 右侧说明 rescan 太多。Nested Loop Join 先看左侧估行准不准偏差大就查统计信息或开动态采样还不行就用/*use_hash(xxx)*/换计划。估行正常但性能差看 Output filters 里有没有用 batch_join。SubPlan Filter 同样先看左侧估行正常的话看有没有用 batch再不行就在 SQL 里找对应子查询考虑用/*unnest*/改写。如果 HASH DISTINCT、SORT、HASH GROUP BY、HASH JOIN 这些算子有 IO TIME说明落盘了可以适当调大sql_work_area_size。对于 INSERT、UPDATE、DELETE、MERGE 类算子要开 PDML 才能并行用/*parallel(xxx) enable_parallel_dml*/。5. 本篇常见错排查报错一obdiag 连不上集群。先确认obdiag config里的 IP、端口、账号密码对不对再确认网络能通。如果用的是非默认配置文件命令里要加-c指定路径。报错二trace_id 查不到数据。V4.0.0 以下版本从gv$sql_audit查V4.0.0 及以上从gv$ob_sql_audit查。如果 SQL 执行完很久了audit 表可能已经刷掉trace_id 就失效了。建议执行完 SQL 立刻取last_trace_id()并采集。报错三display_cursor 拿回来的计划不是目标 SQL 的。大概率是没指定 svr_ip 和 svr_port导致DISPLAY_CURSOR在别的节点上执行。手动调用时务必带上这两个参数或者直接用 obdiag 让它自动填。报错四opt_trace 文件是空的。检查ENABLE_OPT_TRACE是否真的开了以及SET_OPT_TRACE_PARAMETER的 level 是否够。obdiag 默认用 level 3如果手动调用时没设参数可能只记录很少信息。报错五计划里 EST.ROWS 和 REAL.ROWS 差很多。先看 stats info 的 version 是不是很旧是的话手动收集统计信息。如果统计信息是新的但估行还是不准考虑开动态采样/*dynamic_sampling(1)*/或者检查过滤条件里有没有 case when、like 这类优化器难估的表达式。报错六并行度没生效。看 Note 里有没有Degree of Parallelism is N because of hint再看 dop_method 是 Global DOP 还是 TableDOP。如果是 TableDOP说明表定义里设了并行度hint 可能被覆盖。INSERT/UPDATE/DELETE 类还要确认有没有加enable_parallel_dml。6. 把采集和解读串成日常动作实际排查时我的习惯是先在gv$ob_sql_audit里按耗时排序找到问题 SQL拿到 trace_id然后一条obdiag gather dbms_xplan把两类信息收下来。先看 display_cursor 里的 CPU TIME topN 算子定位是哪个算子慢如果怀疑是计划生成阶段的问题再翻 opt_trace 看哪个改写规则或优化步骤耗时异常。大部分单条 SQL 的性能问题这两份文件基本够用了。如果你需要更完整的诊断报告比如包含表结构、系统变量、等待事件等可以配合 obdiag 的其他采集命令一起用。但就 DBMS_XPLAN 这一块来说上面这套流程已经能覆盖慢查询定位和调优的主要场景。需要长期做 SQL 调优和 Agent 辅助编码的话可以了解下 Coding Plan把模型对话和编码工作流接起来会更顺手https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite想直接验证模型对执行计划的解读能力可以在模型对话里贴计划让它帮你分析https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite接入相关的 API Key 和文档在这里https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 和 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite