挂起事务最典型ash特征是sql*net message from client状态伴随零星on cpu样本,表明应用已发完sql但未提交,需结合v$transaction中used_ublk是否持续高位来确认。

查 ASH 里 SQL*Net message from client + 长时间 ON CPU 的组合
未提交事务挂起最典型的 ASH 特征,不是锁等待,而是会话长时间处于 SQL*Net message from client 状态,同时伴随零星但持续的 ON CPU 样本(非等待事件)。这说明应用端已发完 SQL、断开连接或卡在业务逻辑里,但数据库侧事务仍 open,后台进程仍在维护 undo 段或清理缓冲区。
实操建议:
- 运行:
SELECT session_id, sql_id, sql_opname, event, COUNT(*) FROM v$active_session_history WHERE sample_time > SYSDATE - 1/1440 AND (event = 'SQL*Net message from client' OR session_state = 'ON CPU') GROUP BY session_id, sql_id, sql_opname, event ORDER BY COUNT(*) DESC - 重点关注
sql_opname IN ('INSERT', 'UPDATE', 'DELETE')且event = 'SQL*Net message from client'占比超 80% 的会话——它大概率已发完 DML,但没发 COMMIT/ROLLBACK - 若同一
session_id在过去 1 分钟内反复出现ON CPU+ 空event+sql_id = '0000000000000000',说明 undo 分配或回滚段维护正在后台持续消耗 CPU,事务仍未结束
关联 v$transaction 看 used_ublk 是否持续增长
ASH 只是快照,真正确认事务是否“挂起”,得看 v$transaction 中的 used_ublk 是否稳定不降。一个已挂起但未提交的事务,used_ublk 会保持高位(甚至缓慢上涨),而正常短事务该值会在几秒内归零。
实操建议:
- 立刻执行:
SELECT s.sid, s.serial#, s.username, s.program, s.status, t.used_ublk, t.start_time FROM v$session s JOIN v$transaction t ON s.taddr = t.addr WHERE t.used_ublk > 5000 ORDER BY t.used_ublk DESC -
used_ublk > 5000(约 40MB)就值得警惕;若status = 'INACTIVE'且username为空,基本可判定是客户端异常断开后残留的挂起事务 - 对这类会话,不要只查
sql_id——它往往已是'0000000000000000'或空,应直接ALTER SYSTEM KILL SESSION 'sid,serial#'清理
注意 RAC 环境下 blocking_instance 为空却仍有挂起事务
在 RAC 中,一个会话可能在实例 A 上挂起事务,但因网络抖动或客户端静默,v$session.blocking_session 显示为空,blocking_instance 也为空。此时不能误判为“无阻塞”,而要主动查 GV$ACTIVE_SESSION_HISTORY 全局视图,过滤 sql_opname IN ('INSERT','UPDATE','DELETE') 且 event = 'SQL*Net message from client' 的跨实例高频样本。
实操建议:
- 运行:
SELECT inst_id, session_id, sql_opname, COUNT(*) FROM gv$active_session_history WHERE sample_time > SYSDATE - 1/720 AND event = 'SQL*Net message from client' AND sql_opname IN ('INSERT','UPDATE','DELETE') GROUP BY inst_id, session_id, sql_opname HAVING COUNT(*) > 10 - 若某
inst_id下多个session_id同时满足条件,且对应v$session.status = 'INACTIVE',说明该实例存在批量挂起事务风险 - 别依赖
DBA_HIST_ACTIVE_SESS_HISTORY回溯——死锁或挂起事务常在 10 秒内发生并消失,历史表采样间隔太粗,容易漏掉关键窗口
避免把长查询误判为挂起事务
大范围 UPDATE 或 DELETE 执行数分钟是正常的,不能单凭 used_ublk 高或 ON CPU 时间长就 kill。关键区别在于:真实挂起事务的会话,v$session.event 会长期停留在 SQL*Net message from client,而长查询会持续显示 db file sequential read、direct path write 等实际 I/O 或 CPU 活动事件。
实操建议:
- 对高
used_ublk会话,先查v$session.event和seconds_in_wait:若event = 'SQL*Net message from client'且seconds_in_wait > 300,才属可疑挂起 - 用
v$sql查其sql_id对应的语句:含FOR UPDATE或明显缺少COMMIT调用链(如 PL/SQL 包中无异常处理块)的,挂起概率更高 - 检查应用日志——很多挂起事务源于应用层 try/catch 后忘记 rollback,或连接池配置了 auto-commit=false 却未显式控制事务边界
真实挂起事务最难识别的点,是它既不报错、也不阻塞别人(初期),只安静地吃 undo 空间和 PGA 内存;等 DBA 发现 ORA-30036 或内存告警时,往往已有十几个事务在后台空转。所以必须把 ASH 查询和 v$transaction 检查做成自动化巡检项,而不是等报警才动手。











