ASH无法直接记录存储过程本身的I/O消耗,仅通过plsql_entry_object_id和plsql_entry_subprogram_id关联其内部SQL及等待事件;需先查目标过程object_id,再筛选ASH中对应活跃会话,并结合top_level_sql_id回溯调用链。
直接查 v$active_session_history 无法定位“存储过程本身”的 i/o 消耗,因为 oracle 不在 ash 中记录 pl/sql 单元的独立 i/o 统计;它只记录该过程内部执行的 sql 所触发的等待事件和资源消耗。真正能建立过程与 i/o 的关联点,只有 plsql_entry_object_id 和 plsql_entry_subprogram_id 这两个字段。
怎么从 ASH 锁定目标存储过程的活跃会话
关键不是搜过程名,而是先拿到它的 object_id,再反查 ASH 中正在以它为入口执行的会话。过程若在包里,subprogram_id 必须匹配(不是行号,是包体中子程序的声明序号,从 1 开始):
- 查过程
object_id:SELECT object_id FROM all_objects WHERE owner = 'SCOTT' AND object_name = 'CALC_SALES' AND object_type = 'PROCEDURE' - 查 ASH 中匹配的活跃采样:
SELECT sql_id, event, session_state, sample_time FROM v$active_session_history WHERE plsql_entry_object_id = 78901 AND sample_time > SYSDATE - 1/24 - 若过程在包
pkg_report中,还需加条件:AND plsql_entry_subprogram_id = 3(对应包中第三个子程序) - 注意:该过程必须在采样窗口内处于“活跃执行中”——即正在执行内部 SQL 或等待 I/O,刚进就返回或卡在 PL/SQL 解析阶段的不会被捕获
为什么不能直接按 SQL_ID 聚合 I/O 并反推过程名
AWR 和 v$sql 中的 disk_reads、buffer_gets 等指标只归属到 SQL_ID,不携带调用上下文。哪怕某条高读 SQL 是在 pkg_order.process_batch() 里执行的,DBA_HIST_SQLSTAT 也只会显示该 SQL 自身统计,不会打标“来自 process_batch”。所以单靠 SQL 统计无法闭环归因。
- 常见误操作:看到
SQL_ID = 'abc123xyz'的physical reads很高,就去DBA_SOURCE全文搜索语句文本——但动态 SQL(EXECUTE IMMEDIATE)会被标准化或截断,匹配率低 - 更可靠的做法:先用 ASH 锁定该 SQL_ID 在高 I/O 等待时段(
event LIKE 'db file%read%')是否频繁出现在某个plsql_entry_object_id下 -
top_level_sql_id字段很重要:它指向最外层调用者(比如匿名块里的EXEC pkg_order.process_batch),可帮你确认调用链是否合理
如何验证 I/O 是否真由该过程引发而非误判
ASH 提供的是“谁在等 I/O”,不是“谁造成了 I/O”。要排除干扰,需交叉验证三类信息:
- 检查该过程内部是否含全表扫描逻辑:查
DBA_SOURCE中是否有未走索引的SELECT * FROM large_table,或HINT强制FULL - 确认
sql_id对应的执行计划是否稳定:用SELECT * FROM dba_hist_sqlplan WHERE sql_id = 'abc123xyz' AND plan_hash_value IN (SELECT DISTINCT plan_hash_value FROM v$sql WHERE sql_id = 'abc123xyz'),避免因绑定变量窥探导致计划漂移 - 比对时间窗口:ASH 中该
plsql_entry_object_id的采样密集期,是否与 AWR 报告中 “SQL ordered by Reads” 的峰值时段重合?不重合则大概率是噪声 - 特别注意
direct path read类等待:若大量出现在该过程下,极可能触发了并行查询或大结果集临时表写入,这时应查v$sql_workarea看actual_mem_used和max_tempseg_size
最易被忽略的一点:plsql_entry_object_id 只在过程“作为顶层 PL/SQL 单元被调用”时才有效。如果它是被另一个过程嵌套调用的(比如 proc_a 调 proc_b),那么 ASH 中记录的仍是 proc_a 的 object_id,除非显式用 DBMS_APPLICATION_INFO.SET_MODULE 打标。所以调用链深度超过一层时,必须结合 top_level_sql_id 和应用日志才能厘清。


















