ora-01555错误本质是查询所需的一致性读前镜像(undo)被覆盖,主因包括频繁commit加速undo重用、长查询超undo_retention、延迟块清除时事务槽被覆写;解决需组合调大undo空间与retention、优化sql、拆分事务及清理脏块。

PL/SQL循环中每行都commit,反而更容易触发ORA-01555
不是“提交能释放资源”,而是频繁commit会加速UNDO段重用——每次提交后,对应事务的UNDO块就标记为可覆盖;后续长查询需要这些前镜像时,发现已被新事务覆写,直接报ORA-01555。
- 循环里
commit不会缩短UNDO保留时间,反而让UNDO空间更快被其他事务抢占 - 尤其当循环更新大表(如
all_objects)时,每个commit都释放ITL槽、触发延迟块清除,增加一致性读负担 - Oracle 9i+自动UNDO管理下,
undo_retention只是“软目标”,高并发时即使设为10800,实际保留可能远低于该值
FOR UPDATE + 循环处理时,ORA-01555常出现在游标打开后很久才读取数据
典型场景:声明一个长生命周期游标(如SELECT ... FOR UPDATE),然后在循环中逐行UPDATE并commit。问题出在游标打开那一刻,Oracle就锁定了查询快照;后续commit不释放该快照依赖的UNDO,但新事务持续挤压UNDO空间,导致快照过旧。
- 游标打开时间越早,越容易撞上UNDO被覆盖的窗口期
- 即使只更新1行,只要游标已打开且未关闭,整个结果集的一致性读仍需原始UNDO
- 避免方式:把
SELECT和UPDATE拆成两个独立事务,或改用BULK COLLECT+FORALL减少游标持有时间
为什么UNDO_RETENTION调大了还是报错?
undo_retention不是硬保证——它只在UNDO表空间有空闲空间时才生效。一旦UNDO表空间满,Oracle会强制覆盖“过期”UNDO,哪怕还没到设定的秒数。
- 检查
v$undostat中的MAXQUERYLEN字段,它反映当前最老活跃查询的持续时间,若接近或超过undo_retention,说明已逼近临界点 - 用
select sum(bytes)/1024/1024 from dba_data_files where tablespace_name = 'UNDOTBS1';确认UNDO表空间真实大小,小表空间配大undo_retention毫无意义 - 真正有效的是组合动作:
ALTER SYSTEM SET undo_retention=10800;+ALTER DATABASE DATAFILE '...undotbs01.dbf' RESIZE 4G;
延迟块清除(Delayed Block Cleanout)是隐藏雷区
大量INSERT/UPDATE后立即commit,但部分数据块未完成块清除(Fast Cleanout失败),后续长查询访问这些块时,需回查UNDO事务表确认提交SCN——此时若事务槽已被覆盖,就会报ORA-01555,且错误信息里不体现“rollback segment”,只显示“snapshot too old”。
- 这种场景下,优化SQL或调大
undo_retention无效,因为问题不在查询本身,而在前期DML遗留的脏块 - 验证方法:
SELECT name, value FROM v$sysstat WHERE name LIKE '%cleanout%';查看delayed block cleanout计数是否异常高 - 根治手段:对高频DML表,在业务低峰期执行
ANALYZE TABLE t COMPUTE STATISTICS;或DBMS_STATS.GATHER_TABLE_STATS,强制触发块清除
commit释放的是事务锁,不是一致性读所需的UNDO空间;而延迟块清除这种底层机制,根本不会在错误堆栈里明说。











