ash显示sql运行时间异常时,首先要排除采样偏差,再检查执行计划变更、数据分布倾斜及统计信息准确性,关键验证视图包括v$sql、v$sql_plan_statistics_all、dba_tab_statistics和awr历史计划。
ash 显示 sql 运行时间异常,不是采样误差,大概率是执行计划已变、或数据分布严重倾斜,得立刻查 v$sql_plan 和 dba_tab_statistics,别只盯着等待事件。
ASH 里看到某条 SQL 执行耗时 40s,但实际业务只跑几毫秒?先确认是不是采样偏差
ASH 是 1 秒采样一次,v$active_session_history 里一条记录代表“该会话在那一秒处于活跃状态”,不等于“这条 SQL 真的跑了 40 秒”。常见误判场景:
- SQL 实际执行快,但被阻塞(如
enq: TX - row lock contention)后长时间挂起,ASH 把整个阻塞期都算进“SQL 执行时间” - SQL 被硬解析卡住(比如
library cache lock),ASH 记录的是“parse + execute”总耗时,而你只关心 execute 阶段 - SQL 在 PL/SQL 块里循环调用,ASH 每次采样都落在不同迭代上,看起来像单次执行超长
验证方法:查 v$sql 中该 sql_id 的 elapsed_time / executions 平均值,再对比 cpu_time 和 buffer_gets。如果平均 elapsed_time 是 2ms,但 ASH 里有几十条 30s+ 样本,基本可排除真实执行慢,转向查阻塞或解析问题。
执行计划真的变了?用 dbms_xplan.display_cursor 和 dbms_xplan.display_awr 对比
Oracle 12c 的自适应执行计划(Adaptive Plans)和统计信息失效都会导致计划突变,ASH 里集中出现某条 SQL 的 direct path read 或 gc current block 2-way,往往是计划退化信号。
- 查当前内存中计划:
select * from table(dbms_xplan.display_cursor('84m7xzxz0181g', null, 'outline +peeked_binds'));—— 注意看是否用了全表扫描、是否走了错误的连接方式 - 查 AWR 历史计划:
select * from table(dbms_xplan.display_awr('84m7xzxz0181g'));—— 对比故障前后 plan_hash_value 是否一致 - 重点检查
access_predicates和filter_predicates:如果分区裁剪没生效(比如用了函数 on partition key),会导致扫全分区
特别注意:display_cursor 查不到结果 ≠ SQL 没执行过,可能是已被 aged out;display_awr 查不到则说明当时没被捕获到快照,得结合 dba_hist_sql_plan 手动查。
数据倾斜让执行计划“看起来合理,实际卡死”
计划没变、统计信息也新,但某次执行突然巨慢——十有八九是绑定变量值触发了数据倾斜。例如 where status = :b1,:b1=‘A’ 时返回 10 行,:b1=‘Z’ 时返回 500 万行,优化器按平均值估算,选了 nested loop,结果遇到‘Z’就崩。
- 查绑定变量窥探痕迹:
select child_number, peeked, executions, buffer_gets from v$sql where sql_id = '84m7xzxz0181g';如果peeked = 'YES'且executions很大但buffer_gets波动剧烈,就是典型信号 - 查具体值分布:
select endpoint_actual_value, endpoint_number from dba_histograms where owner='YOUR_SCHEMA' and table_name='YOUR_TABLE' and column_name='STATUS';看 ‘Z’ 是否落在高频 bucket 里 - 临时缓解:加
/*+ OPT_PARAM('_optim_peek_user_binds','false') */关闭窥探,或对倾斜列建扩展统计信息dbms_stats.create_extended_stats
别信“统计信息刚收集过就一定准”——分区表删了某个分区但没重收集该分区统计信息,num_rows 还是旧值,优化器照样误判。
为什么 v$sql_plan_statistics_all 比 ASH 更能定位“真慢点”
ASH 告诉你“谁慢”,v$sql_plan_statistics_all 告诉你“哪一步慢”。它记录每次执行的实际行数(output_rows)、实际耗时(last_elapsed_time)、物理读(last_disk_reads),是唯一能定位到计划节点级性能拐点的视图。
- 查最耗时的步骤:
select operation, options, last_starts, last_output_rows, last_elapsed_time/1000000 elapsed_sec from v$sql_plan_statistics_all where sql_id = '84m7xzxz0181g' order by last_elapsed_time desc; - 如果某 step 的
last_starts是 1 但last_output_rows是 0,说明谓词没下推、过滤无效;如果last_output_rows远大于cardinality,说明统计信息严重不准 - 注意:该视图只保留最近几次执行的统计,且需开启
statistics_level = ALL(默认为 TYPICAL),否则字段全为空
真正难缠的问题,往往藏在“计划没变、统计信息看着也新、ASH 只显示等待事件”的夹缝里——这时候必须落到 v$sql_plan_statistics_all 的每一行输出上,看数字是否说谎。











