unexpired状态堆积是空间卡住的直接表现,根本原因是tuned_undoretention被自动调优大幅拉高,导致已提交事务的undo块长期无法回收;验证需查v$undostat中该值是否远超手动设置的undo_retention,且须结合nospaceerrcnt、autoextensible状态及dba_undo_extents分布综合判断。

UNEXPIRED状态堆积是空间卡住的直接表现
查DBA_UNDO_EXTENTS看到大量UNEXPIRED、极少EXPIRED,就说明undo块没被回收——不是Oracle坏了,而是它认定这些块“还没到期”。根本原因不是事务没结束,而是TUNED_UNDORETENTION被拉得太高,远超你设的undo_retention值。
验证方式很简单:
SELECT MAX(TUNED_UNDORETENTION) FROM v$undostat;
如果返回值是几万秒(比如 345600 = 4 天),而你只设了 900 秒,那基本就是_undo_autotune在起作用。
-
_undo_autotune=true是 Oracle 10.2+ 默认行为,MMON 每 30 秒根据最长查询时间(MAXQUERYLEN)动态推高保留阈值 - 一旦出现长查询(报表、ETL),
TUNED_UNDORETENTION就被顶上去,已提交事务的 undo 块就一直卡在UNEXPIRED - 哪怕
v$rollstat.XACTS = 0(无活跃事务),空间也动不了
数据文件非自动扩展加剧空间冻结
当DBA_DATA_FILES.AUTOEXTENSIBLE = 'NO'时,Oracle 会把空间当稀缺资源保护起来:宁可延长保留时间防ORA-01555,也不轻易覆盖UNEXPIRED块。结果就是表空间使用率冲到 95%+,但dba_undo_extents里几百个UNEXPIRED段纹丝不动。
关键信号不止看占用率,要看这三处:
-
V$UNDOSTAT.NOSPACEERRCNT > 0:说明真的缺空间,不是参数没生效 -
DBA_DATA_FILES.BYTES不变,但DBA_UNDO_EXTENTS持续新增UNEXPIRED - 监控发现
undo change vector size突增,对应某批 JOB 或 SQL 执行
关闭_autotune或换新UNDO表空间才真正有效
调undo_retention本身没用——只要_undo_autotune=true开着,它就被无视。能落地的操作只有两个:
- 永久禁用自动调优:
alter system set "_undo_autotune" = false scope=spfile;,必须重启才生效 - 新建表空间并切换:
CREATE UNDO TABLESPACE undotbs2 DATAFILE '/path/undotbs2.dbf' SIZE 2G AUTOEXTEND ON;→alter system set undo_tablespace = undotbs2;
注意:旧UNDOTBS1不能立刻删,得等DBA_UNDO_EXTENTS.STATUS里没有ACTIVE或UNEXPIRED再执行drop tablespace ... including contents。
高水位导致resize失败不是undo问题,是空间管理逻辑
即使切换了新UNDO,老表空间想resize却卡在 20GB 下不去?这不是undo没回收,而是某个回滚段的最后一个区段(extent)物理位置太靠后,抬高了整个表空间的高水位(HWM)。dba_extents里BLOCK_ID最大的那个段,就是瓶颈。
绕过它的唯一安全做法是:
- 绝不复用原表空间名重建(控制文件里残留元数据会导致
ORA-01119) - 要么换全新路径建新UNDO,要么用
REUSE强制覆盖原文件 - offline drop 损坏数据文件前,必须先用
MANUAL模式启动,避免SMON加载损坏段
真正的难点从来不在命令怎么写,而在判断哪一段UNEXPIRED是被长查询拖住的、哪个datafile的HWM卡死了resize、以及重启前有没有确认NOSPACEERRCNT归零。这些点漏掉一个,操作就可能半途失败。











