Oracle 问题定位和分析:卡慢SQL排查
从等待事件到执行计划,基础起步-看这一篇就够了
半夜告警响不停,SQL跑得慢吞吞数据库里千行泪,皆因等待苦争奔锁住资源不肯放,IO瓶颈把人坑且看老夫七板斧,定位元凶破迷津——深夜DBA有感-Acdante
凌晨3点,监控大屏突然变红:"数据库响应时间 > 30秒"!你从被窝里爬出来,手忙脚乱地敲着键盘,心里一万只草泥马奔腾而过。打开V$SESSION一看,一堆会话在那儿"等待"着,也不知道在等什么。最近又被再次翻出来的麻园诗人的泸沽湖洗脑了。这神奇的旋律虽然早几年就在听,但也是属于小众歌曲,这段时间,又火了一次,神奇的旋律,放在文章开头,有兴趣可以听听哈。
这场景,每个DBA都经历过。说实话,80%的性能问题都是SQL惹的祸。今天我就把压箱底的SQL排查功夫全部传授给你,从等待事件到执行计划,从绑定变量到统计信息,保证你看完就能定位那些"卡死"你的慢SQL。现在也有很多这样的性能检测平台和各类工具,如聚好看团队的 DBDoctor如白鳝老师的性能分析平台等,都可以非常直观的探测到各类性能问题。本文多个好用的 SQL 来源于黄廷忠老师的分享,他的个人主页:www.htz.pw以及他的公众号:大家也可以关注一下。对了以下脚本,均为真实实战 SQL,可运行。当然简单看 SQL 状态和当前运行 SQL,还有一个最最简单的方式:如果有条件使用 Oracle自己的官方工具-SQLDeveloper,直接链接后,就有各种 SQL 监视器可以直观可视化。黄老师的SQL对于 11g是直接可用,分享内的SQL文件都是原版文件。我文章内的基本都适配19c了,已经更新适配调整过,也整理成txt了。有需要可以后台回复“SQL”即可获取。
IT民工的龙马人生
黄廷忠,公众号:IT民工的龙马人生SQL优化实战:标量子查询改写外连接的真实案例
📌 本文目标:理解等待事件本质 + 掌握6大类排查SQL + 建立系统化SQL诊断思路。

01.等待事件——数据库的"体温计"
很多人问我:"SQL跑得慢,到底慢在哪儿了?"我的回答是:看等待事件!
Oracle的等待事件,就是数据库的"体温计"。正常情况下,体温36.5度;发烧了,38度。等待事件也是一样,正常运行的数据库,等待很少;出问题了,等待事件就会告诉你"哪儿疼"。
等待事件分类——先分清敌我
Oracle的等待事件成千上万,但可以分为几大类,这里只是列出几个常见的和本人用分析的几个大类等待事件,大家有补充可以交流讨论:
| User I/O | db file scattered read | |
| Concurrency | gc buffer busy | |
| Application | enq: TM - contention | |
| System I/O | log file parallel write | |
| Network | SQL*Net more data to client | |
| Configuration | free buffer waits |
核心视图:V$SESSION_WAIT 和 V$SESSION
V$SESSION_WAIT
是Oracle最常用的实时等待视图,每一秒都在刷新,记录着当前正在等待的会话。V$SESSION
则包含了会话的完整信息,包括当前SQL、等待事件等。可以说查会话、查性能、查 SQL 多半是离不开这两个视图的,也是实时性能分析最常用的两个视图。
⚠️ 注意:从Oracle 10g开始,V$SESSION_WAIT的大部分信息已经合并到V$SESSION中,所以直接查V$SESSION就行,别再费劲查两个视图了!
02.从等待事件定位问题SQL——六步定位法
知道了等待事件分类,接下来就是实战。我总结了六步定位法,从宏观到微观,逐步缩小范围。

03.排查实战SQL——拿走就能用
公众号 SQL 格式有点问题复制进来,需要完整脚本我整理成txt了,有需要关注后发送“SQL”获取
第一类:TOP等待事件分析
1.1 数据库TOP等待事件(累计)

输出示例:可以看看我这个库等待事件能看出什么明细,哈哈。
1.2 等待类统计(按类别汇总)

输出示例:
第二类:当前活跃会话分析
2.1 当前等待中的会话(按等待事件分组)

输出示例:
2.2 活跃会话详情(包含SQL_ID和等待信息)

输出示例:
2.3 [RAC] 全局活跃会话(RAC环境)

输出示例:
第三类:IO等待专项分析(db file相关)
db file sequential read = 索引/单块读
db file scattered read = 全表扫描/多块读
3.1 IO等待会话(重点关注db file类事件)

输出示例:暂无3.2 热块分析(buffer busy waits)

暂无04.SQL性能分析——V$SQL和V$SQLSTATS
找到SQL_ID后,下一步就是分析SQL性能。V$SQL和V$SQLSTATS是最常用的SQL性能视图。
4.1 关键字段解读
| ELAPSED_TIME | ||
| CPU_TIME | ||
| BUFFER_GETS | ||
| DISK_READS | ||
| ROWS_PROCESSED | ||
| EXECUTIONS | ||
| FETCHES | ||
| SORTS | ||
| PLAN_HASH_VALUE |
4.2 SQL性能统计查询(基于V$SQLSTATS)

输出示例:
4.3 指定SQL_ID的性能指标(sql_stat_by_sqlid核心逻辑)
📎 脚本参考来源:黄廷忠(htz),http://www.htz.pw,做了 19c适配

输出示例:
05.绑定变量窥探——还原真实SQL
找到慢SQL后,你看到的SQL文本可能是这样的:
SELECT * FROM orders WHERE customer_id = :1 AND status = :2什么鬼?:1
和 :2
是啥值?这就是绑定变量的"锅"。Oracle为了复用执行计划,把具体值替换成了占位符。
问题来了:有时候执行计划选错了,恰恰就是因为这个绑定变量的"特殊值"触发了绑定变量窥探,导致走了错误的执行计划!
5.1 查看绑定变量值(V$SQL_BIND_CAPTURE)
根据SQL_ID获取绑定变量值
-- 核心查询:从V$SQL_BIND_CAPTURE获取绑定变量 
⚠️ 注意:V$SQL_BIND_CAPTURE默认15分钟采样一次,如果SQL刚执行完,可能采样不到!这时候要看V$SQL_MONITOR。
5.2 从V$SQL_MONITOR获取实时绑定变量
-- 从V$SQL_MONITOR的BINDS_XML获取绑定值(针对特定会话) 
绑定变量窥探的坑:Oracle在第一次执行时,会根据实际传入的值来决定执行计划。如果第一次传入的值是"极端值"(比如MIN或MAX),后续即使传正常值,也可能沿用那个极端的执行计划!
06.执行计划分析——定位性能瓶颈
执行计划是SQL优化的"地图"。看不懂执行计划,就像开车没有导航,只能瞎开。
6.1 获取执行计划的方法
方法1:DBMS_XPLAN.DISPLAY_CURSOR(从库缓存)
-- 查看指定SQL的执行计划(需要SQL_ID) SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&SQL_ID', NULL, 'ALLSTATS LAST'));方法2:DBMS_XPLAN.DISPLAY_AWR(AWR历史)
-- 查看AWR中保存的执行计划(需要SQL_ID) SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&SQL_ID'));方法3:查看当前会话的SQL执行计划
-- 查看当前SESSION正在执行的SQL计划 SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));6.2 SQL Monitor报告
SQL Monitor是Oracle企业版的"神器",能看到实时执行的详细信息,包括各步骤的等待时间、IO统计等。
查看SQL Monitor报告(文本格式)
-- 查看SQL Monitor统计信息 
查看SQL执行进度(实时监控)
-- 长时间运行的SQL执行进度 
6.3 ASH执行计划统计
ASH(Active Session History)记录了活动会话的历史快照,可以帮我们分析执行计划各步骤在过去一段时间的等待情况。
-- ASH执行计划统计(最近1小时的采样统计) 
07.大杀器:sql10综合诊断脚本
说了这么多SQL,有没有一个脚本能一键生成完整的诊断报告?有的!这就是sql10,来自Oracle圈著名DBA黄廷忠(网名"认真就输")。
📎 sql10.sql 功能清单:
✅ 完整SQL文本(含绑定变量替换)
✅ 执行计划(从库缓存/AWR)
✅ ASH执行计划统计
✅ SQL Monitor报告
✅ V$SQLSTATS/V$SQL统计信息
✅ 表信息、索引信息
✅ 列统计信息(含MIN/MAX值)
✅ 分区信息(支持CDB)
sql10脚本核心功能展示
📎 脚本来源:黄廷忠(htz),http://www.htz.pw
sql10使用方式(简化版)
-- sql10.sql 使用方式 -- 方式1:传入SQL_ID @sql10.sql SQL_ID -- 方式2:传入SQL_ID和优化器环境 @sql10.sql SQL_ID OPTIMIZER_ENV_ID -- 方式3:开启列MIN/MAX值收集(检测谓词越界) @sql10.sql SQL_ID '' '' '' '' '' '' _TABLE_COL_VALUEsql_stat_by_sqlid(sql10子模块)——SQL统计信息
-- sql_stat_by_sqlid.sql 核心查询逻辑 -- 从V$SQLSTATS获取性能指标
sql_fulltext_by_sqlid(sql10子模块)——绑定变量替换
-- sql_fulltext_by_sqlid.sql 核心逻辑 -- 从V$SQL_BIND_CAPTURE获取绑定变量 
08.谓词越界与统计信息——容易被忽视的坑
很多SQL跑着跑着突然变慢了,排查半天发现是谓词越界问题。
什么是谓词越界?
假设有个表 orders
,按 order_date
建了索引。
表的订单日期范围:2020-01-01 ~ 2024-01-01 统计信息记录的MAX值:2024-01-01 某天新插入了一批订单,日期:2024-06-15 - 但是统计信息还没更新!
这时候如果有个SQL:WHERE order_date > '2024-06-01'
,
Oracle以为最多只有1天的数据(因为MAX是2024-01-01),可能选择索引扫描;
实际上有半年的数据!结果就是——索引扫描反而更慢!
8.1 查看列统计信息(含MIN/MAX)
-- 查看表的列统计信息 

8.2 查看列MIN/MAX值(数值类型)
-- 从RAW类型转换获取实际MIN/MAX值 
8.3 sql10的列统计信息收集(_TABLE_COL_VALUE参数)

8.4 统计信息过时检测


-- 统计信息过期检查 + 安全的表增长估算(19c 适配)


⚠️ 统计信息过期警告:
- 建议生产环境设置自动统计信息收集(默认窗口)
- 大批量数据变更后(>10%行数变化)应手动收集统计信息
- 使用 DBMS_STATS.GATHER_TABLE_STATS
收集
- 关键表可使用 CASCADE=>TRUE
同时收集索引统计
09.索引缺失与低效诊断
索引是SQL优化的"万金油",但索引缺失或低效也是性能问题的常见原因。
9.1 查看表的索引信息
-- 查看表的索引
9.2 查看索引列
-- 查看索引的列 
9.3 未建索引的外键(容易导致锁等待)
-- 查找未建索引的外键列(父表DELETE时会锁住子表) 
10.排查思路总结——六步定位法完整版
好了,所有SQL都介绍完了,最后来一张完整的大图,帮助你建立系统化的排查思维:

常见问题的快速判断
SQL排查路漫漫,等待事件是明灯执行计划细分析,绑定变量见真情统计信息若过时,优化器也会发疯六步定位走天下,从此不怕慢SQL待到山花烂漫时,告警不再扰美梦——DBA修行之路,Acdante作
总结
SQL性能问题排查的核心是从等待事件到SQL文本,从执行计划到统计信息的全链路分析。
遇到慢SQL,记住六步定位法:
查等待 → 找会话 → 分析性能 → 执行计划 → 还原SQL → 定位根因。
善用sql10等综合工具,结合本文的排查SQL,你也能成为SQL优化的"老中医"。
📎 本文引用脚本说明基于hzt老师的脚本,做了 Oracle 19c的适配:
sql_fulltext_by_sqlid.sql sql_fulltext_mem_by_sqlid.sql
/ sql_stat_by_sqlid.sql / sql10.sql
作者:黄廷忠(htz),http://www.htz.pw
公众号:IT民工的龙马人生
也欢迎关注我的主页:https://acdante.com和公众号:
📌 关注公众号 Acdante,获取更多Oracle/MySQL技术干货~
本文SQL脚本基于Oracle 19c验证,适用于单机及RAC环境,需要脚本,公众号后台回复“SQL”获取
Oracle生产级别备份脚本分享——逻辑备份和物理备份(expdp&RMAN)【Oracle数据库分享--0x01】
Oracle数据加密技术演进与实践指南从10g到26ai的安全之道【Oracle数据库分享--0x02】
Oracle数据库表空间与数据文件实战指南【Oracle数据库分享--0x03】
Oracle ADG单机部署实战指南【Oracle数据库分享--0x04】
Oracle DataGuard搭建信息收集清单和ADG自动化部署脚本(单机版本)免费开放【Oracle数据库分享--0x05】
Oracle表空间使用率自动检测与智能扩容实战——Shell脚本监控告警及自动扩展数据文件与ASM磁盘组管理基础知识【Oracle数据库分享--0x06】
Oracle 26ai数据库架构体系结构与新特性浅显分享【Oracle数据库分享--0x15】




