快速定位Oracle长SQL执行瓶颈需查V$SQL_PLAN配合ALLSTATS LAST,重点关注STARTS与A-Rows偏差大的步骤;慎拆含ROWNUM、函数谓词或UNION ALL的子查询;GATHER_PLAN_STATISTICS后须立即调用DISPLAY_CURSOR获取真实统计。
怎么快速定位 Oracle 长 SQL 的执行瓶颈
oracle 里跑得慢的长 sql,光看语句长度没用,关键得看它实际怎么走的。直接查 v$sql 和 v$sql_plan 是最快路径,但别只盯着 elapsed_time——那个是总耗时,掩盖了真正卡点。
- 先用
SQL_ID定位:从V$SESSION或 AWR 报告里捞出正在执行/刚结束的慢 SQL 的SQL_ID - 查计划时加
FORMAT => 'ALLSTATS LAST'(用DBMS_XPLAN.DISPLAY_CURSOR),否则看不到真实 I/O 和行数偏差 - 特别注意
STARTS和A-Rows差距大的步骤:比如STARTS=1000但A-Rows=1,大概率是嵌套循环里驱动表没走索引,被重复执行了上千次
长 SQL 拆解时哪些子查询不能随便提出来
不是所有 WITH 子句或内联视图都能安全“提前计算”。Oracle 优化器对复杂嵌套的代价估算经常失真,强行拆可能让原本能下推的谓词失效,反而多扫几百万行。
- 含
ROWNUM、FETCH FIRST或分析函数OVER (ORDER BY ...)的子查询,提成WITH后可能改变逻辑结果 - 关联条件里带函数(如
TO_CHAR(col) = :bind)的子查询,提出来后索引无法使用,执行计划可能从 INDEX RANGE SCAN 变成 FULL TABLE SCAN - 如果子查询里有
UNION ALL且各支路数据量差异极大,合并后优化器可能选错驱动顺序,建议保留原结构并加/+ USE_CONCAT /提示
格式化长 SQL 时最容易破坏执行计划的操作
加换行、空格、注释本身不影响解析,但改写过程中常误触几个隐性雷区:
- 把
WHERE a.id = b.id(+)改成LEFT JOIN语法:Oracle 9i 以后虽支持,但旧版本提示(如/+ ORDERED /)可能不生效,且外连接方向容易翻转 - 用工具自动重排
AND条件顺序:优化器对谓词顺序敏感,尤其当列有直方图时,把高选择性条件放前面才能触发索引跳过扫描(INDEX SKIP SCAN) - 替换绑定变量为字面量(如把
:v1改成'ABC')测试性能:硬编码会生成新SQL_ID,执行计划完全不可比,还可能因字符集隐式转换引发全表扫描
为什么加了 /*+ GATHER_PLAN_STATISTICS */ 还看不到真实执行数据
这个提示只是开启采集开关,不等于数据自动吐出来。漏掉下一步,等于白加。
- 执行完 SQL 后必须立刻调用
DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'),延迟几秒再查,V$SQL_PLAN_STATISTICS_ALL里的统计就可能被刷掉 - 如果 SQL 被硬解析多次(比如应用没用绑定变量),
DISPLAY_CURSOR默认只显示最后一次,得传入具体SQL_ID和CHILD_NUMBER - RAC 环境下,执行节点和查计划节点不一致时,
V$SQL_PLAN_STATISTICS_ALL只存本实例数据,跨节点要连到对应实例查
有些长 SQL 的性能拐点藏在统计信息陈旧、绑定变量窥探失效、甚至临时表空间争用里——这些不会直接写在执行计划里,得结合 V$SESSTAT 里 physical reads 和 temp space allocated 一起看。











