好多年不做ADG恢复了,生疏了
背景
物理机故障重启,上面的虚拟机跟着重启,虚拟机里的 Oracle ADG 备库也跟着重启了。这本来是个很常规的操作,相关人报给我说无法启动。
当时看到出错信息是 ORA-10458 的时候,心里咯噔了一下——备库打不开了。第一反应是完了,是不是得重做备库。冷静下来一步步排查,发现根本不用重做,问题比想象的简单,但隐蔽得很。
一、问题背景
这是一套 Oracle 11.2.0.4 的 Active Data Guard 环境:主库 prod-oracle-186177(db_unique_name=rhino),备库 prod-oracle-186178(db_unique_name=rhinoadg),都跑在 VMware 虚拟机上,虚拟机跑在同一台物理机上。
那天物理机出了点故障,重启了。虚拟机跟着重启,Oracle 实例也跟着起来了。但备库起来之后,ALTER DATABASE OPEN 直接报错:
ORA-16016: archived log for thread 1 sequence# 53800 unavailable
ORA-10458: standby database requires recovery
ORA-01196: file 1 is inconsistent due to a failed media recovery session
ORA-01110: data file 1: '/data/oradata/rhino/system01.dbf'
看到这个错误链,第一反应是备库在重启的时候,正在应用 sequence 53800 的 redo,重启把这个过程打断了,datafile 1 处于 fuzzy 状态。现在 53800 这段 redo 丢了,Crash Recovery 完不成,所以 OPEN 不了。
二、排查过程
2.1 确认缺失的 sequence
先看告警日志,确认重启前后的时间线:
Mon Sep 28 11:12:23 2026
Media Recovery Waiting for thread 1 sequence 53800 (in transit)
Recovery of Online Redo Log: Thread 1 Group 5 Seq 53800 Reading mem 0
Mem# 0: /data/oradata/data/standby_redo05.logMon Sep 28 13:05:37 2026
Starting ORACLE instance (normal)
11:12 的时候 MRP 在等 53800(in transit,正在传输中),13:05 实例重启。中间这段 53800 的 redo 没传完,重启后丢了。
在备库上确认 MRP 状态和 GAP:
SELECT process, status FROM v$managed_standby WHERE process LIKE'MRP%';
-- no rows selected MRP 没在跑SELECT*FROM v$archive_gap;
-- no rows selected Oracle 没检测到 GAP
GAP 没查出来,但 53800 确实不在备库。因为 GAP 检测依赖于备库知道"我应该有哪些 sequence",而 53800 是在传输途中丢的,备库的控制文件里可能还没记录。
2.2 主库确认 53800 归档还在不在
-- 主库上
SELECT sequence#, name, archived, deleted, status, completion_time
FROM v$archived_log
WHERE sequence# =53800AND thread# =1;-- 结果:
-- SEQUENCE#=53800
-- NAME=/data/dblog/1_53800_1120748561.dbf
-- ARC=YES DEL=NO STATUS=A COMPLETION_T=28-SEP-26
主库说 53800 已经归档了,DEL=NO(RMAN 没删)。然后去主库 /data/dblog/ 目录 ls 看了一下,文件确实在:
-rw-r----- 1 oracle dba 110381056 Sep 28 11:54 1_53800_1120748561.dbf
好,文件还在。那就好办了,要么手动拷过来注册,要么让 FAL 自动推。
2.3 发现监听没起来,这其实才是关键所在,没有把监听设置成自动重启
先试试能不能让 FAL 自动推。在备库上拉起 MRP:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECTFROM SESSION;
然后等了一会儿,查 53800 有没有到:
SELECT sequence#, name, applied FROM v$archived_log WHERE sequence# >=53800ORDERBY sequence#;
-- no rows selected
没来。MRP 在 WAIT_FOR_LOG 等 53800,但主库推不过来。
去主库查一下为什么推不过来:
SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;-- DEST_ID=2
-- STATUS=ERROR
-- ERROR=ORA-12541: TNS:no listener
ORA-12541——备库监听没起!物理机重启后,虚拟机里的 Oracle 实例虽然启动了(因为 /etc/oratab 配了自动启动),但监听没有跟着自动启动。
在备库上启动监听:
lsnrctl start
lsnrctl status
监起来了,但等了一会儿 53800 还是没来。再去主库查:
SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;-- DEST_ID=2
-- STATUS=ERROR
-- ERROR=ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
12541 变成了 12514。监听起来了,但主库连不上备库的服务。这就是问题的真正根因了。
三、原理分析:为什么 MOUNT 状态下主库连不上备库
3.1 备库的 service 注册机制
Oracle 实例启动后,PMON 进程会自动把实例的 service_name 注册到监听。但注册哪些 service_name 取决于实例的状态:OPEN 状态(READ ONLY)下注册 db_name 和 db_unique_name 两个服务,MOUNT 状态下(未 OPEN)只注册 db_unique_name 一个服务。
这套环境的参数如下表所示。
备库 OPEN 时,监听里同时有 rhino 和 rhinoadg 两个 service。备库 MOUNT 时,监听里只有 rhinoadg。
3.2 TNS 配置的隐患
主库的 tnsnames.ora 里,备库的 TNS 别名是这样的:
RHINOADG =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST=10.60.186.178)(PORT=1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = rhino)
)
)
看到没有?SERVICE_NAME = rhino,用的是 db_name 而不是 db_unique_name。备库 OPEN 时,监听里有 rhino 服务,主库能连,一切正常。备库 MOUNT 时,监听里只有 rhinoadg,主库用 rhino 去连,ORA-12514。
这是个潜伏已久的配置隐患。平时备库都是 OPEN 状态跑着 ADG,这个问题根本看不出来。一旦备库重启停在 MOUNT,就暴露了。
而且这个隐患很有迷惑性:tnsping 备库别名是通的(tnsping 只测 TCP 连通性,不验证 service),但主库 ARC 进程实际连的时候就会报 ORA-12514。
3.3 完整的故障因果链
把整个问题串起来:
物理机故障重启
→ 虚拟机重启
→ Oracle 实例自动启动(oratab 配了 Y)
→ 但监听没自动启动(监听没有配置)
→ 主库 log_archive_dest_2 连不上备库(ORA-12541)
→ 53800 redo 没传到备库
→ 备库 Crash Recovery 需要 53800 但本地没有
→ ORA-16016 → ORA-10458 → 无法 OPEN
而修好监听后还有第二层问题:
监听起来了
→ 但 TNS SERVICE_NAME 配的是 db_name(rhino)不是 db_unique_name(rhinoadg)
→ 备库 MOUNT 状态只注册了 rhinoadg
→ 主库用 rhino 连不上(ORA-12514)
→ FAL 无法自动推送 53800
两层问题叠加,导致了一个看似简单实则绕了两道弯的故障。
四、解决过程
4.1 修复 TNS 配置
在主库上修改 tnsnames.ora,把 SERVICE_NAME 从 rhino 改成 rhinoadg:
cd$ORACLE_HOME/network/admin
cp tnsnames.ora tnsnames.ora.bak_20260928
vi tnsnames.ora
修改后:
RHINOADG =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST=10.60.186.178)(PORT=1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = rhinoadg)
)
)
测试:
tnsping rhinoadg
# OK (0 msec)
4.2 触发主库重连
tnsping 通了,但 v$archive_dest 还报 ORA-12514,因为状态有缓存。不想改参数,用切日志的方式触发重连:
-- 主库上
ALTERSYSTEM ARCHIVE LOG CURRENT;SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;
-- DEST_ID=2
-- STATUS=VALID
-- ERROR=(空)
STATUS 变 VALID 了。
4.3 FAL 自动补日志
链路通了之后,主库 ARC 进程通过 FAL 把 53800 推给了备库。在备库上监控:
SELECT sequence#, name, applied FROM v$archived_log WHERE sequence# >=53800ORDERBY sequence#;-- 53800 /data/dblog/1_53800_1120748561.dbf YES
-- 53801 /data/dblog/1_53801_1120748561.dbf YES
-- 53802 /data/dblog/1_53802_1120748561.dbf YES
-- 53803 /data/dblog/1_53803_1120748561.dbf YES
-- 53804 /data/dblog/1_53804_1120748561.dbf YES
-- 53805 /data/dblog/1_53805_1120748561.dbf YES
53800~53805 全部 applied=YES,MRP 追上来了:
SELECT process, status, thread#, sequence#, delay_mins
FROM v$managed_standby WHERE process LIKE'MRP%';
-- MRP0 WAIT_FOR_LOG 1 53806 0
4.4 OPEN + 切实时应用
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
ALTER DATABASE OPEN;
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USINGCURRENT LOGFILE DISCONNECTFROM SESSION;
4.5 最终验证
SELECT name, database_role, open_mode FROM v$database;
-- RHINO PHYSICAL STANDBY READ ONLY WITH APPLYSELECT process, status, delay_mins FROM v$managed_standby WHERE process LIKE'MRP%';
-- MRP0 APPLYING_LOG 0SELECT name, valueFROM v$dataguard_stats WHERE name IN ('transport lag','apply lag');
-- transport lag +00 00:00:00
-- apply lag +00 00:02:28(追上后归零)SELECT*FROM v$archive_gap;
-- no rows
ADG 完全恢复。
五、本来以为要重做,为什么不用?
一开始看到 ORA-10458 + ORA-01196 的时候,第一反应是备库 datafile 已经 fuzzy 了,可能得重做。仔细分析后发现不用。
53800 的归档在主库还在。 如果主库也丢了,那只能走 RMAN 增量 SCN 前滚,甚至重做。但这次主库归档完好,问题只是没传过来。
datafile fuzzy 是可逆的。 Crash Recovery 失败不代表 datafile 损坏了,只是缺少需要的 redo。补上这段 redo,Crash Recovery 就能完成,fuzzy 状态就清除了。
FAL 机制可以自动补。 只要修通网络链路(监听 + TNS),主库会自动把缺失的归档推过来,不需要手动 scp。
真正需要重做的场景是:主库归档丢了 + 备库 datafile 损坏 + 没有 RMAN 备份。这次三个条件一个都不满足。
六、总结和教训
6.1 监听要加入开机自启
物理机重启 → 虚拟机重启 → Oracle 实例自动启动了(oratab 配了 Y),但监听没有。这是个很容易忽略的点。
检查方法:
# /etc/oratab 里最后一列是 Y 表示实例会自动启动
# 但监听的自动启动需要额外配置
cat /etc/oratab | grep -v "^#"# 确认 dbstart 脚本里有没有启动监听
cat$ORACLE_HOME/bin/dbstart | grep lsnrctl
如果没有,可以在 dbstart 脚本里加上 lsnrctl start,或者用 systemd service 管理。
6.2 TNS SERVICE_NAME 必须用 db_unique_name
这是个非常隐蔽的配置问题。很多人的 ADG 环境里 TNS 都配的是 db_name,平时跑得好好的,一旦备库重启停在 MOUNT 就暴露了。
检查方法:在主库上执行 cat $ORACLE_HOME/network/admin/tnsnames.ora | grep -A 8 备库别名,确认 SERVICE_NAME 的值等于备库的 db_unique_name:
-- 备库上
SHOWPARAMETER db_unique_name
如果不一致,改成 db_unique_name。改完即时生效,不需要重启。
6.3 备库重启标准流程
以后重启备库,必须按这个顺序来:
停 MRP: ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;确认无 GAP: SELECT * FROM v$archive_gap;(无结果再继续)shutdown immediatestartup mount起监听: lsnrctl start主库确认链路通: SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;(STATUS=VALID)备库 OPEN: ALTER DATABASE OPEN;拉实时应用: ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;验证: open_mode=READ ONLY WITH APPLY
核心原则:先停 MRP 再 shutdown,确保没有 in-transit 的 redo 被杀掉。先起监听再 OPEN,确保主库链路已通。
6.4 v$archive_dest 状态有缓存延迟
改完 TNS 后,v$archive_dest 的状态不会立即刷新,有约 1 分钟延迟。不要用 ALTER SYSTEM SET log_archive_dest_2 来"刷新"——那是在改参数,不是刷新状态。用 ALTER SYSTEM ARCHIVE LOG CURRENT 切一次日志,ARC 进程会重新连接备库,状态自然就刷新了。
6.5 按说以前如果是我的风格一定是配置对的,但是这不是我安装的。而且很多年不做了生疏了。
这次恢复过程中整理的诊断和恢复流程,已经写成了 skill 文件,包含三条恢复路径:路径A是修通链路后 FAL 自动补日志(本次走的这条路),路径B是手动 scp 归档 + REGISTER LOGFILE,路径C是 RMAN 增量 SCN 前滚(归档丢了的情况)。以后再遇到类似问题,让agent做吧。