ash不记录触发器单独执行,只记录父sql的pl/sql调用链;需通过pl/sql cpu占比>30%、等待事件及关联dba_triggers定位可疑触发器,并禁用对比验证。

ASH 里根本看不到触发器执行记录
Oracle 的 ASH(Active Session History)不会单独采样或标记触发器本身。它只记录会话在“SQL 执行”或“PL/SQL 执行”状态下的堆栈,而触发器逻辑被包裹在父 SQL 的 PL/SQL 调用链中——你看到的永远是 UPDATE t SET x=1 这条语句的采样,不是 tr_log_after_update 的执行耗时。
这不是采样遗漏,是 Oracle 内部调度机制决定的:触发器属于父语句事务上下文内的 PL/SQL 子例程,ASH 不做函数级切片,只保留最外层可识别的 SQL_ID 和 PLSQL_ENTRY_OBJECT_ID。
- 查
v$active_session_history时,sql_id指向的是触发它的 DML,不是触发器内部语句 -
plsql_entry_object_id可能为空,或指向父过程(如包),而非触发器名 - 即使触发器内有慢查询,
ASH也不会把该子查询的sql_id单独暴露出来
怎么从 ASH 推断触发器正在拖慢 DML
关键不是找“触发器”,而是找“本不该慢却变慢”的 DML 在 PL/SQL 时间占比异常高。
运行以下查询,重点看 plsql_exec_time 占比和等待事件:
SELECT sql_id,
sql_opname,
COUNT(*) AS samples,
ROUND(AVG(time_waited), 2) AS avg_wait_ms,
ROUND(SUM(CASE WHEN session_state = 'ON CPU' AND plsql_entry_object_id IS NOT NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) AS plsql_cpu_pct
FROM v$active_session_history
WHERE sample_time > SYSDATE - 1/24
AND sql_opname IN ('UPDATE', 'INSERT', 'DELETE')
GROUP BY sql_id, sql_opname
HAVING SUM(CASE WHEN session_state = 'ON CPU' AND plsql_entry_object_id IS NOT NULL THEN 1 ELSE 0 END) > 0.3 * COUNT(*)
ORDER BY plsql_cpu_pct DESC;
- 如果某条 DML 的
plsql_cpu_pct> 30%,且sql_opname是简单操作(如单行UPDATE),基本可判定 PL/SQL 部分(含触发器)占主导 - 配合
event字段:若出现enq: TX - row lock contention或db file sequential read且集中在 PL/SQL 栈中,说明触发器里可能有 SELECT FOR UPDATE 或无索引查询 - 不要只盯
sql_id—— 同一sql_id在不同时间点的plsql_entry_object_id可能不同,需关联dba_objects查名称
结合 DBA_TRIGGERS 和 ASH 定位具体触发器
有了可疑 sql_id 和高 PL/SQL 占比后,下一步是锁定表和触发器。不能靠猜,得查系统视图反推。
先确认该 DML 操作了哪些表:
SELECT DISTINCT object_owner, object_name
FROM v$sql_plan
WHERE sql_id = '&your_sql_id'
AND operation = 'TABLE ACCESS'
AND options IN ('FULL', 'BY INDEX ROWID', 'CLUSTER');
再查这些表上启用的触发器:
SELECT trigger_name, triggering_event, status FROM dba_triggers WHERE table_owner = '&owner' AND table_name = '&table_name' AND status = 'ENABLED' ORDER BY trigger_name;
- 重点关注
AFTER EACH ROW类型——它们在每行变更后都执行,批量操作时开销指数放大 - 若触发器名含
audit、log、sync,优先怀疑其内部 INSERT/SELECT 是否缺少索引 -
triggering_event为UPDATE OR INSERT的复合触发器,容易因未加 WHERE 条件导致全表扫描
真正有效的验证动作不是看 ASH,而是禁用+对比
ASH 提供的是间接线索,不是因果证据。唯一能确认“就是它”的方式,是隔离验证。
- 用
ALTER TRIGGER tr_name DISABLE临时关闭嫌疑触发器(注意:需有 ALTER ANY TRIGGER 权限) - 在相同负载下重放原 DML,用
DBMS_MONITOR.SESSION_TRACE_ENABLE抓取两次 trace,对比tkprof输出中的 recursive calls 和 execute count - 特别关注
recursive cpu used和consistent gets差值:若禁用后这两项下降 50% 以上,基本坐实 - 禁止在生产直接禁用所有触发器——有些是约束实现(如外键级联),禁用会导致数据不一致
最常被忽略的一点:ASH 显示的“PL/SQL time 高”,不等于触发器代码写得差;更可能是它调用了没索引的视图、或隐式转换导致全表扫描——问题不在触发器体,而在它访问的对象结构上。











