ORA-01555在ADG只读库发生根本原因是主库UNDO_RETENTION过短或未启用RETENTION GUARANTEE,导致备库查询所需的历史UNDO被覆盖;需主库调大UNDO_RETENTION并启用RETENTION GUARANTEE,同时确保UNDO表空间自动扩展。
ORA-01555 在 ADG 只读库上到底为什么发生
adg(active data guard)备库开启只读模式后,select 查询报 ora-01555: snapshot too old,根本原因不是备库没开归档或没应用日志,而是主库的 undo 保留时间太短,而备库查询需要访问“过去某个 scn”的数据版本——这个版本在备库回放日志时已被主库 undo 段覆盖掉。
关键点:ADG 备库的只读查询依赖主库产生的 UNDO 记录(通过归档日志/实时传输传递),但这些 UNDO 不会单独存一份在备库;它靠的是主库 UNDO 表空间中尚未被覆盖的旧块镜像。一旦主库 UNDO_RETENTION 设置过小、或 UNDO 表空间自动扩展受限、或高并发 DML 导致 UNDO 快速重用,就必然触发该错误。
怎么调 UNDO_RETENTION 才真有用
UNDO_RETENTION 是参数,但不是“保证保留时长”的铁律——它只是建议值。Oracle 实际是否保留,取决于 UNDO 表空间是否启用了 RETENTION GUARANTEE。
- 不加
RETENTION GUARANTEE:即使设了UNDO_RETENTION=3600,只要表空间紧张,Oracle 仍会覆盖旧 UNDO,ORA-01555 照样发生 - 加了
RETENTION GUARANTEE:UNDO 块至少保留指定秒数,哪怕表空间满也宁愿报ORA-30036(无法扩展 UNDO 表空间),而不是破坏一致性 - 必须同时检查
UNDO_TABLESPACE是否为自动扩展(AUTOEXTEND ON),否则 guarantee 会很快失败
执行示例:
ALTER SYSTEM SET UNDO_RETENTION = 3600 SCOPE=BOTH; ALTER TABLESPACE undotbs1 RETENTION GUARANTEE;
备库侧能做的唯一缓解动作:限制长查询
主库 UNDO 优化是治本,但备库本身不能改主库参数。你唯一可控的是让查询别拖太久——因为查询越久,它需要的“历史 SCN”就越老,撞上被覆盖 UNDO 的概率越高。
- 避免在 ADG 备库跑未加
WHERE条件的全表扫描,尤其是大表 - 禁用未绑定变量的长 SQL(容易硬解析+执行计划抖动,拉长一致性读链)
- 对已知慢查询,可临时加
/*+ OPT_PARAM('_serial_direct_read' 'never') */防止直接路径读跳过 buffer cache 中的 CR 块,但这只是权宜之计 - 真正可靠的做法:在备库上用
DBMS_FLASHBACK.ENABLE_AT_SYSTEM_CHANGE_NUMBER显式指定一个较新的 SCN,缩小一致性读窗口
最容易被忽略的兼容性陷阱
Oracle 12c 及以后版本引入了 INMEMORY 和 RESULT_CACHE,它们在 ADG 备库默认关闭,但如果手动启用,可能绕过正常的 UNDO 依赖路径,导致看似“不报错”,实则返回非一致结果——比如缓存了主库某次提交前的中间状态。
- 检查
V$PARAMETER中result_cache_mode是否为MANUAL或FORCE(ADG 上应保持OFF) - 确认
INMEMORY_QUERY参数在备库为DISABLE(19c 默认如此,但升级后可能继承主库值) - 特别注意物化视图日志(
MLOG$)和DBMS_MVIEW.REFRESH类操作:它们内部会隐式打开长时间一致性读,在 ADG 上极易触发 ORA-01555,且错误堆栈里未必直接体现
UNDO 问题从来不是孤立的配置项,它是主备之间 SCN 流水线、内存结构复用、甚至统计信息陈旧度共同作用的结果。调一个参数不如看清整个回放与查询的时间差在哪里。

















