执行计划突变导致awr中physical writes暴增,根本原因是计划退化引发连锁i/o行为——如全表扫描、hash join溢出至temp表空间、嵌套循环驱动表错误等,使实际落盘量激增;需通过dba_hist_sqlstat、dba_hist_sql_plan及ash联合对齐plan_hash_value、disk_reads_delta、instance_number和sample_time定位根因。
执行计划突变如何让awr里physical writes暴增
执行计划突变本身不直接写磁盘,但会触发连锁反应——比如从索引扫描退化为全表扫描+临时表排序,或nlj变成hash join导致大量temp空间写入。awr的physical writes统计的是实际落盘量,不是逻辑写,所以一旦计划选错,i/o吞吐量会在dba_hist_sysstat或dba_hist_seg_stat里立刻拉高。
- 全表扫描 + 无缓存LOB列 → 每次读都触发
db file sequential read,且无法重用buffer,物理读飙升 - HASH JOIN或GROUP BY未走内存 → spill到temp表空间,
physical writes在DBA_HIST_SEG_STAT中显示为TEMP段暴涨 - 嵌套循环走错误驱动表 → 驱动侧返回10万行,被驱动侧索引重复访问10万次,单次快、总量高,
disk_reads在v$sql里体现为“小SQL但I/O极高” - 计划退化后绑定变量窥探失效 → 多个子游标并存,每个都独立做物理读,
parse_calls和disk_reads同步上扬
怎么确认是执行计划变导致的I/O激增
不能只看AWR里Physical Reads/Writes总量,要锁定到具体SQL和计划变更时间点。关键动作是把DBA_HIST_SQLSTAT的executions_delta、disk_reads_delta和DBA_HIST_SQL_PLAN的plan_hash_value三者对齐。
- 查
DBA_HIST_SQLSTAT:找disk_reads_delta突增时段(比如某小时从500涨到12万),记下sql_id和snap_id - 查
DBA_HIST_SQL_PLAN:用同一snap_id范围查该sql_id是否出现新plan_hash_value,且旧计划消失 - 交叉验证:用
DBMS_XPLAN.DISPLAY_AWR对比两个plan_hash_value,重点看access路径是否从INDEX RANGE SCAN变成TABLE ACCESS FULL,或NESTED LOOPS变成HASH JOIN - 排除干扰:如果
elapsed_time_delta没涨但disk_reads_delta翻倍,基本可断定是I/O路径恶化,而非SQL本身变慢
RAC环境下容易漏掉的I/O归因点
RAC节点间计划不一致时,I/O压力可能只出现在某个实例,但AWR默认聚合所有节点,导致总量异常却找不到根因SQL。必须分实例查,否则DBA_HIST_SEG_STAT里的physical_writes会掩盖真实热点。
- 别用
DBA_HIST_SEG_STAT直接查表名,要加instance_number过滤,否则看到的是四节点叠加值 -
gv$active_session_history比v$active_session_history更准:用event = 'db file sequential read'+sql_id+inst_id定位哪台机器在扛I/O - 检查
DBA_HIST_SQLSTAT.INSTANCE_NUMBER是否和DBA_HIST_SNAPSHOT.INSTANCE_NUMBER严格JOIN,漏掉会导致I/O统计跨实例错配 - SecureFiles LOB写入在RAC中若未启用
cache,各节点独立刷脏块,physical_writes会成倍放大
为什么AWR快照可能“看不见”真正的I/O峰值
AWR每小时采样一次,而执行计划突变引发的I/O洪峰往往集中在几分钟内。如果快照刚好错过这个窗口,disk_reads_delta会显示为0,但v$sql的disk_reads和last_active_time会暴露真相。
- 查
v$sql:执行SELECT sql_id, disk_reads, executions, last_active_time FROM v$sql WHERE sql_id = 'xxx' ORDER BY last_active_time DESC,看最近5分钟disk_reads是否陡增 - 结合
v$session:找当前state = 'WAITING'且event = 'db file sequential read'的会话,抓出它们的sql_id和blocking_session - ASH补漏:用
SELECT sql_id, COUNT(*) FROM gv$active_session_history WHERE sample_time > SYSDATE - 1/24 AND event = 'db file sequential read' GROUP BY sql_id ORDER BY 2 DESC,这是唯一能捕获秒级I/O爆发的视图 - 注意
DBA_HIST_SEG_STAT的physical_writes是累计值,不是增量,必须用相邻两个快照相减才能看出小时级变化
真实问题往往卡在“计划变了但没人知道”,而不是“I/O高了但找不到SQL”。AWR提供的是证据链,不是结论;把plan_hash_value、disk_reads_delta、instance_number和sample_time这四个字段对齐,才是定位根因的最小闭环。











