Undo空间爆满主因是长事务、Undo保留时间不足或表空间配置过小,需通过分段提交、逻辑拆分、异步化及定位高Undo消耗SQL来解决。

Undo空间爆满时的典型错误信息
看到 ORA-01555: snapshot too old 或 ORA-30036: unable to extend segment in undo tablespace,基本可以确定是事务太长、Undo保留时间不够,或 Undo 表空间本身配小了。这不是数据库“卡”,而是它在严格执行一致性读和回滚保障——你没给它留够空间和时间。
缩短单个事务周期的实操要点
长事务不等于大数据量操作,而常源于循环里反复 UPDATE 却不提交,或游标遍历中混入耗时逻辑。关键不是“少做”,而是“分段做、及时收”:
- 避免在 PL/SQL 循环内累积大量 DML 后一次性
COMMIT;每处理 1000–5000 行就COMMIT(具体值看单行大小和 Undo 消耗) - 用
BULK COLLECT+FORALL替代逐行FETCH+UPDATE,减少上下文切换和 Undo 生成频次 - 确认业务是否真需要长事务:比如报表导出过程中的临时标记更新,可改用
/*+ APPEND */直接路径插入临时表,绕过 Undo
逻辑拆分比技术调优更有效
很多人盯着 UNDO_RETENTION 和 undo_tablespace 大小调参,但真正压垮 Undo 的,往往是把“查+算+改+发通知”全塞进一个事务。拆分不是为了好看,是为了让每个环节的 Undo 生命周期可控:
- 把数据准备(如
CREATE GLOBAL TEMPORARY TABLE填数)和业务更新分离,前者可设ON COMMIT DELETE ROWS,不占 Undo - 异步化非核心动作:发消息、写日志、调外部接口,全部移出主事务,用 DBMS_SCHEDULER 或队列表后续处理
- 对超大范围更新(如全表重算),用
ROWID分片 +DBMS_PARALLEL_EXECUTE,每片独立事务,失败只回滚该片
检查 Undo 压力的真实来源
别只看 V$UNDOSTAT 里的 UNXPSTEALCNT,那只是“被迫覆盖”的结果。要定位谁在吃 Undo:
- 查长时间未提交会话:
SELECT sid, serial#, sql_id, logon_time, used_ublk FROM v$session s JOIN v$transaction t ON s.saddr = t.ses_addr ORDER BY t.start_time - 确认哪些 SQL 生成最多 Undo:
SELECT sql_id, sum(undoblks) FROM v$transaction t JOIN v$session s ON t.ses_addr = s.saddr JOIN v$sqlarea a ON s.sql_id = a.sql_id GROUP BY sql_id ORDER BY 2 DESC -
ALTER SYSTEM KILL SESSION 'sid,serial#'前先SELECT status, state FROM v$session WHERE sid = ...,确保不是正在做 checkpoint 或 recovery










