首页 > 数据库 > Oracle > 好多年不做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 不了。
先看告警日志,确认重启前后的时间线:
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.log Mon 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 是在传输途中丢的,备库的控制文件里可能还没记录。
-- 主库上SELECT sequence#, name, archived, deleted, status, completion_timeFROM v$archived_logWHERE sequence# = 53800 AND 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 自动推。
先试试能不能让 FAL 自动推。在备库上拉起 MRP:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
然后等了一会儿,查 53800 有没有到:
SELECT sequence#, name, applied FROM v$archived_log WHERE sequence# >= 53800 ORDER BY 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。监听起来了,但主库连不上备库的服务。这就是问题的真正根因了。
Oracle 实例启动后,PMON 进程会自动把实例的 service_name 注册到监听。但注册哪些 service_name 取决于实例的状态:OPEN 状态(READ ONLY)下注册 db_name 和 db_unique_name 两个服务,MOUNT 状态下(未 OPEN)只注册 db_unique_name 一个服务。
这套环境的参数如下表所示。
| 参数 | 主库 | 备库 |
|---|---|---|
| db_name | rhino | rhino |
| db_unique_name | rhino | rhinoadg |
| service_names | rhino | rhinoadg |
备库 OPEN 时,监听里同时有
rhino 和
rhinoadg 两个 service。备库 MOUNT 时,监听里只有
rhinoadg。
主库的 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。
把整个问题串起来:
物理机故障重启 → 虚拟机重启 → 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
两层问题叠加,导致了一个看似简单实则绕了两道弯的故障。
在主库上修改 tnsnames.ora,把 SERVICE_NAME 从
rhino 改成
rhinoadg:
cd $ORACLE_HOME/network/admincp 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)
tnsping 通了,但
v$archive_dest 还报 ORA-12514,因为状态有缓存。不想改参数,用切日志的方式触发重连:
-- 主库上ALTER SYSTEM ARCHIVE LOG CURRENT;SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;-- DEST_ID=2-- STATUS=VALID-- ERROR=(空)
STATUS 变 VALID 了。
链路通了之后,主库 ARC 进程通过 FAL 把 53800 推给了备库。在备库上监控:
SELECT sequence#, name, applied FROM v$archived_log WHERE sequence# >= 53800 ORDER BY 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_minsFROM v$managed_standby WHERE process LIKE 'MRP%';-- MRP0 WAIT_FOR_LOG 1 53806 0
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;ALTER DATABASE OPEN;ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;
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, value FROM 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 备份。这次三个条件一个都不满足。
物理机重启 → 虚拟机重启 → Oracle 实例自动启动了(oratab 配了 Y),但监听没有。这是个很容易忽略的点。
检查方法:
# /etc/oratab 里最后一列是 Y 表示实例会自动启动# 但监听的自动启动需要额外配置cat /etc/oratab | grep -v "^#"# 确认 dbstart 脚本里有没有启动监听cat $ORACLE_HOME/bin/dbstart | grep lsnrctl
如果没有,可以在 dbstart 脚本里加上
lsnrctl start,或者用 systemd service 管理。
这是个非常隐蔽的配置问题。很多人的 ADG 环境里 TNS 都配的是 db_name,平时跑得好好的,一旦备库重启停在 MOUNT 就暴露了。
检查方法:在主库上执行
cat $ORACLE_HOME/network/admin/tnsnames.ora | grep -A 8 备库别名,确认
SERVICE_NAME 的值等于备库的
db_unique_name:
-- 备库上SHOW PARAMETER db_unique_name
如果不一致,改成 db_unique_name。改完即时生效,不需要重启。
以后重启备库,必须按这个顺序来:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;SELECT * FROM v$archive_gap;(无结果再继续)shutdown immediatestartup mountlsnrctl startSELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;(STATUS=VALID)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,确保主库链路已通。
改完 TNS 后,
v$archive_dest 的状态不会立即刷新,有约 1 分钟延迟。不要用
ALTER SYSTEM SET log_archive_dest_2 来"刷新"——那是在改参数,不是刷新状态。用
ALTER SYSTEM ARCHIVE LOG CURRENT 切一次日志,ARC 进程会重新连接备库,状态自然就刷新了。
这次恢复过程中整理的诊断和恢复流程,已经写成了 skill 文件,包含三条恢复路径:路径A是修通链路后 FAL 自动补日志(本次走的这条路),路径B是手动 scp 归档 + REGISTER LOGFILE,路径C是 RMAN 增量 SCN 前滚(归档丢了的情况)。以后再遇到类似问题,让agent做吧。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/637517/viewspace-3184464/ ,如需转载,请注明出处,否则将追究法律责任。