四海内皆兄弟

好多年不做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.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 是在传输途中丢的,备库的控制文件里可能还没记录。

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 一个服务。

这套环境的参数如下表所示。

参数
主库
备库
db_name
rhino
rhino
db_unique_name
rhino
rhinoadg
service_names
rhino
rhinoadg

备库 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 APPLY

SELECT process, status, delay_mins FROM v$managed_standby WHERE process LIKE'MRP%';
-- MRP0  APPLYING_LOG  0

SELECT 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 备库重启标准流程

以后重启备库,必须按这个顺序来:

  1. 停 MRP:ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
  2. 确认无 GAP:SELECT * FROM v$archive_gap;(无结果再继续)
  3. shutdown immediate
  4. startup mount
  5. 起监听:lsnrctl start
  6. 主库确认链路通:SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;(STATUS=VALID)
  7. 备库 OPEN:ALTER DATABASE OPEN;
  8. 拉实时应用:ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;
  9. 验证: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做吧。

Image