运维管理13 分钟阅读
一条烂 SQL 干爆一个库,Oracle 也顶不住!
大家好,这里是公众号 DBA学习之路,分享一些学习数据库路上的知识和经验。 @TOC目录 下午有开发同事反馈,一条原本执行时间在 1 秒以内的 SQL 突然延长至 16 秒,严重影响了产线业务的正常运行。经过我分析和优化
2025年11月28日
oraclerac
DBA 实战课程
系统学习 Oracle DBA
- 2738+ 篇实战文章
- 1500+ 步骤截图
- 10 大核心模块
开通会员 ¥128/月起
查看完整课程在知识库中专注阅读,并随时返回相关工具与课程
大家好,这里是公众号 DBA学习之路,分享一些学习数据库路上的知识和经验。

本章目标:掌握本章核心知识点 前置要求:完成前序章节学习 预计时长:60 分钟
下午有开发同事反馈,一条原本执行时间在 1 秒以内的 SQL 突然延长至 16 秒,严重影响了产线业务的正常运行。经过我分析和优化之后,SQL 恢复正常,数据库性能提升 99.99%。

本文将详细记录整个问题的定位过程、优化方法及相关思考。
数据库环境为 Oracle 19C RAC CDB 架构(运行了 6 个 PDB),故障发生时间为 2025.11.27 13:11:09.227。
初步沟通后得知,开发人员通过应用日志监控到某条 SQL 执行时间显著增加,导致接口超时,进而影响业务流程。
首先,通过 SQLID 3jm7s0g3w2px0 获取该 SQL 的 AWR SQL 报告(awrsqrpt)。当然,也可以使用 dbms_xplan.display_cursor 来获取执行计划:
1SELECT * FROM TABLE(dbms_xplan.display_cursor('3jm7s0g3w2px0', NULL));通过 AWR SQL 报告分析,可以发现该 SQL 存在两个执行计划:

慢的执行计划采用了全表扫描方式:

从等待可以看出,SQL 执行时间主要消耗在 I/O 等待上:

快的执行计划则使用了索引访问:

索引访问的 I/O 等待明显减少:

基于经验判断,这很可能是由于执行计划选择错误导致的性能问题,解决方向是固定最优执行计划。
可以使用 coe_xfr_sql_profile 脚本来固定执行计划:
1-- 参数1:SQL_ID,参数2:需要绑定的执行计划哈希值2SQL> @coe_xfr_sql_profile 3jm7s0g3w2px0 1000005031执行后会在当前目录生成 coe_xfr_sql_profile_3jm7s0g3w2px0_1000005031.sql 脚本,执行该脚本即可完成执行计划绑定:
1SQL> @coe_xfr_sql_profile_3jm7s0g3w2px0_1000005031验证绑定结果:
1SQL> SET lines 222 PAGES 10002COL name FOR a603COL status FOR a104COL type FOR a105SELECT name, status, created, type FROM dba_sql_profiles;67NAME STATUS CREATED TYPE8------------------------------------------------------------ ---------- --------------------------------------------------------------------------- ----------9coe_3jm7s0g3w2px0_1000005031 ENABLED 27-NOV-25 03.08.33.103163 PM MANUAL执行计划绑定后,开发反馈 SQL 执行速度恢复正常,接口不再超时,产线业务得以恢复。
虽然表面问题已解决,但我进一步采集了 AWR 报告进行深入分析,结果发现了更严重的性能问题。
报告采集时间范围为 60 分钟,DB Time 高达 1,592.57 分钟,平均活动会话数(Avg Active Sessions)达到 23.6,表明数据库在此期间承受了巨大的性能压力:

Load Profile 显示每秒物理读达到 121,964.1 个块,Read I/O 吞吐量为 952.8MB/s,其中直接路径读(Direct Reads)占比超过 96%,达到 934.242M/s:

Top Event 中 direct path read 等待事件占比 63.5%,总等待时间 60,694 秒,平均每次等待 35.60 ms,远超 10ms 的健康标准:

Time Model 分析显示 sql execute elapsed time 占 DB Time 的 98.33%,而 DB CPU 仅占 8.43%,进一步证实性能瓶颈主要在 I/O 等待:

进一步分析发现,SQL ID 为 08n0j9b7uw4pv 的语句物理读高达 4.32 亿次,占总量的 98.12%,执行频繁且主要等待事件为直接路径读,表明该 SQL 对大表进行了大量全表扫描操作,是系统最主要的性能瓶颈:

当 Oracle 在执行大表扫描时会自动启用直接路径读机制,以避免污染 Buffer Cache。但当该等待事件占比过高且平均延时超标时,则说明数据库中存在高频执行的大表全表扫描 SQL。
这个库的 SQL 得垃圾到什么程度啊,大量全表扫描直接读取磁盘,造成严重 I/O 压力。
该 SQL 的优化方案很简单:为 WHERE 条件中的字段创建联合索引。优化后的执行计划显示将使用索引范围扫描:
1------------------------------------------------------------------------------------2| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |3------------------------------------------------------------------------------------4| 0 | SELECT STATEMENT | | 1 | 33 | 3 (0)| 00:00:01 |5| 1 | SORT AGGREGATE | | 1 | 33 | | |6|* 2 | INDEX RANGE SCAN| IDX_XXXXXX_XXX | 1 | 33 | 3 (0)| 00:00:01 |7------------------------------------------------------------------------------------这个问题也就立刻解决了。
回到最初的问题 SQL,我们使用 sqlhc 工具进行深入分析:
1$ sqlplus / as sysdba @sqlhc T '3jm7s0g3w2px0'分析发现,慢的执行计划平均执行时间为 7.3s,明显比开发说的 16s 要低:

仔细观察可以发现,从 10:00 开始,该执行计划的执行时间从原来的 0.5s 左右激增至 198s 左右,且主要时间都消耗在 I/O 等待上,且 I/O 等待时间呈现逐渐增长趋势:

这表明最初 SQL 的性能下降并非单纯由于多个执行计划,而是受到了系统级 I/O 压力的间接影响。虽然该 SQL 本身存在多个执行计划也是一个问题,但根本原因在于系统中存在大量全表扫描操作导致的 I/O 资源竞争。
我将上述分析结果反馈给开发团队后,我建议优化全表扫描的 SQL,并为其增加合适的联合索引。然而,开发团队对此提出了几点质疑:
经过半小时的友好深入沟通,我逐一解释了这些问题:
最终,开发团队接受了我的建议。
随后,我创建了联合索引之后,晚上抓了一个 AWR 报告看了下数据库性能:



多么健康的数据库,那条 SQL 也早已从 Top SQL 中消失。所以,回到我们最初的标题——“一条烂 SQL 也能拖垮一个数据库!” 这绝不是危言耸听。
说实话,这次问题排查给我上了挺生动的一课。刚开始我也以为就是个简单的执行计划跑偏的问题,绑个 profile 就完事了。谁知道越挖越深,最后发现是个“连环案”。
想想也挺有意思的——开发同事看到的是“我的 SQL 怎么突然慢了”,我一开始看到的是“这个 SQL 怎么走错索引了”,但真正的问题却是“整个数据库的 IO 都被打满了”。就像医院里来了个病人说头疼,结果一查是高血压引起的,再一查发现是肾脏出了问题。
最后想说的是,这次问题虽然解决了,但我心里还是有点没底——谁知道下一个“IO 杀手”会什么时候出现呢?或许我们应该趁这个机会,好好梳理一下整个数据库的 SQL 质量了。毕竟,治标不如治本啊。
