Aekor

Aekor
专注于用户阅读体验的响应式博客主题
  1. 首页
  2. Oracle 数据库
  3. 正文

Oracle 19c DG 容灾静默失效 5 天:一次密码文件引发的复盘

2026-10-05 13635点热度 0人点赞 0条评论

这是一次"容灾看起来一直在,实际早就没了"的故障。主库 10-01 09:40 存储控制器故障,DBA 按惯例准备切换,才发现备库的 apply lag 已经 21 小时 07 分、GAP 41 个序列、传输目的地状态是 ERROR / ORA-16191。再往前翻,传输在 09-26 04:30 就断了,中间 5 天没有任何人收到有效告警。

根因不复杂:安全平台在 09-26 02:00 轮换了 SYS 口令,而 01:30 值班脚本按月度备份窗口把 LOG_ARCHIVE_DEST_STATE_2 置成了 DEFER,口令变更的 redo 没能传到备库;04:30 备份结束、脚本恢复 ENABLE 时,主库用新口令、备库用旧密码文件,认证直接失败。之后的 5 天里,DG 巡检脚本把 apply lag 标为 P4,安全平台把"备库口令仍是旧强度"生成了低优先级工单,两条救命信号都被搁置。

本文按时间线、现象、13 分钟决策、ORA-16191 原理、双端取证、修复与改进项展开。环境为 Oracle 19.3.0.0 + RU 19.21 单机主库 + 同城物理备库,无 Broker,LGWR ASYNC。

故障概览

故障概览

项目数值 / 结论
数据库版本Oracle 19.3.0.0 + RU 19.21(CDB,2 PDB:PAYPDB / SETPDB)
架构单机物理主库 + 同城物理备库 DG(无 Broker,LGWR ASYNC;原文"非容器化部署"指非 Docker/K8s,非 CDB 含义)
主库业务中断2h18m(10-01 09:40 ~ 11:58,存储故障,期间无库可切)
容灾失效窗口09-26 02:00 ~ 10-01 09:40(原文记 4 天 21 小时,见文末待核对),期间备库实际不可用
Redo 传输中断时长09-26 04:31 ~ 10-01 13:47(原文记 116 小时,见文末待核对)
最大 Apply Lag21 小时 07 分(故障暴露时点)
GAP 规模41 个归档序列,约 187 GB redo
RPO0(选择修复主库保住全部数据,代价是 RTO 从分钟级变为 2h18m)
修复 DG 传输耗时8 分钟(拷贝密码文件 + 重启目的地)
追平 Lag 耗时1h47m
根本原因SYS 口令轮换(12.2+ 密码文件 redo 自动传输机制)与备库传输 DEFER 窗口重叠 → 传输丢失 → ORA-16191 认证死锁;叠加监控盲区静默 5 天
触发事件10-01 主库存储控制器故障 → 需要切换时发现备库 lag 21h,failover 被拒

环境架构

环境架构

主库 paycore 与备库 paycore_stb 之间是同城专线(独立 VLAN 310,ASYNC)。主库侧密码文件 orapwPAYCORE 的 mtime 是 09-26 02:00,md5 为 7c3a..e91f;备库侧 mtime 是 08-14 11:22,md5 为 1b88..04c2,两端不一致。

备库的归档目的地配置为 LOG_ARCHIVE_DEST_2,备份窗口由人工脚本控制;"每月最后一个周六"做一次密码文件手工同步,这就是备库文件停留在 8 月 14 日的原因。

故障时间线

故障时间线

时间事件可见性
8 月 14 日上一次手工拷贝密码文件到备库(季度演练遗留),此后双端文件一致—
09-26(六)01:30值班脚本按月度备份窗口 runbook 执行 ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_2=DEFER(2023 年起的历史遗留步骤,"避免备份期间传输抖动影响快照")工单闭环
09-26(六)02:00安全平台(口令保险库)按等保密评整改计划自动轮换 SYS 口令:主库执行 ALTER USER SYS IDENTIFIED BY ...,密码文件 mtime 更新;该变更 redo 因 DEST_2 DEFER 无法送达备库平台显示"整改成功"
09-26(六)04:30备份结束,脚本 ENABLE DEST_2;LNS 重连备库,立即 ORA-16191 / 备库侧 ORA-01017,目的地进入 ERROR 态监控全绿
09-26 ~ 09-30GAP 逐日累积;MRP 把存量归档应用完后转入 WAIT_FOR_GAP。每日"DG 巡检脚本"输出的 apply lag 在 09-28 起超阈值,但告警规则将该项标为 P4("备库只读,不紧急")无人认领
09-29(二)安全平台周扫描报告:"备库节点 SYS 口令仍为旧口令强度,整改未完成" → 自动生成低优先级工单,派单池无人响应(这是第二次被浪费的救命信号)P4 搁置
10-01(四)09:40主库存储控制器 B 故障切换,叠加微码缺陷导致 I/O 挂起 46 秒 → LGWR 写超时,实例 abort,CRS 未部署(裸单机,无 Oracle Restart)全站交易中断
2026/10/1 9:47DEA 到场,判断存储短时间不可恢复,启动无 Broker 手工 failover 预案—
2026/10/1 9:52切换前置四件套检查发现:Apply Lag 21h07m、GAP 41 个序列 → failover 意味着丢 21 小时支付数据,当场否决最坏时刻
2026/10/1 10:05决策:放弃切换,全力修复主库(更换控制器 / 路径切换)—
2026/10/1 11:58主库 OPEN,崩溃恢复完成,业务恢复(中断 2h18m,RPO=0)业务恢复
2026/10/1 12:20开始处理 DG:定位 ORA-16191,备库 alert 对应 ORA-01017—
2026/10/1 13:47scp 主库密码文件到备库 → 重启传输目的地 → FAL 自动拉取 GAP → MRP 续起(传输修复仅 8 分钟)—
2026/10/1 15:34Apply 追平,lag 归零,容灾能力恢复—

故障现象

故障现象

视角现象
应用侧09:40 起全站交易超时,WebLogic 连接池 Connection timed out,支付成功率 99.98% → 0%
主库 DB 侧alert log: LGWR: I/O error 3113 on log file,随后 Instance terminated by PMON;服务器存活、监听存活、实例死亡
存储侧阵列日志:控制器 B 硬件告警 → 自动 failover 到控制器 A → 切换瞬间 I/O 挂起 46s(微码已知缺陷,厂商公告过但未安排升级)
备库 DB 侧一切"正常":实例 OPEN READ ONLY WITH APPLY,MRP 进程在,只是 WAIT_FOR_GAP 了 5 天
监控侧全绿(主机存活、端口通、MRP 进程存在三项探针全部正常)

这份表最值得记住的是最后两行:备库"看起来正常",监控"看起来全绿"。进程存活和端口连通,无法证明数据在流动。

应急响应与决策

13 分钟切换决策

切换决策

09:47 DEA 到场,09:52 拿到切换前置检查结果,10:05 定案。从"发现无库可切"到"决定修主库"是 13 分钟,评估了五个方案:

方案预期结果决策理由
A. 立即 failover(ACTIVATE STANDBY)5~10 分钟恢复业务否决丢 21 小时支付交易数据,资金对账不可接受;RPO 击穿等于用确定性的资损换时间
B. 先恢复传输、追平 lag 再 switchover数据零丢失后平滑切换否决(当时)密码文件修复 + 187GB redo 传输 + 应用追平,乐观估计 2.5~4 小时,与修主库时间相当且不确定性更高(当时根因未明)
C. 修复主库存储,原地拉起中断取决于硬件修复采纳控制器备件现场有货,厂商 4 小时 SLA,历史同类维修 1~3 小时;RPO=0 确定
D. 重启主库实例再观察秒级验证否决I/O 层挂起,实例起来也会再次写超时 abort,无意义且徒增崩溃恢复次数
E. 激活备库只读承接查询分流减轻"全盲"状态部分采纳报表/对账本来就在备库跑,无需变更;但数据是 21 小时前的,不得用于任何资金操作

复盘结论:方案 C 是当时约束下的正确选择(实际中断 2h18m,优于 B 的不确定性),但它"正确"得极其侥幸,如果主库是数据文件级损坏而非控制器故障,修复时间将不可控。真正的问题不是决策质量,而是 5 天前就没有退路了。

无 Broker 时手工 failover 的顺序

这次因为 lag 太大没有执行,但顺序值得固定下来,避免下次临场翻文档:

1. 确认主库确实不可恢复(或修复时间超出 RTO)
2. 在备库查 v$dataguard_stats、v$archive_gap、v$archive_dest
3. 评估 GAP 对应的业务数据能否接受丢失,形成书面结论
4. 停止应用写入,冻结所有指向主库的连接
5. 备库取消 MRP:ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
6. 应用完已收到的归档:ALTER DATABASE RECOVER MANAGED STANDBY DATABASE FINISH;
7. 激活:ALTER DATABASE ACTIVATE STANDBY DATABASE;
8. 打开:ALTER DATABASE OPEN;
9. 校验数据字典、业务对账,再切连接串

第 3 步是唯一不能由 DBA 单独决定的环节。本例 21 小时的支付数据,任何"先切了再说"的方案都过不了资金对账这一关。

主库恢复后 DG 修复

DG 修复

12:20 到 15:34 的修复过程:

步骤操作耗时结果
1主库 alert log 定位 ORA-16191,备库 alert 对应 ORA-0101715 min定性"redo 传输认证失败"
2比对双端密码文件:md5sum 不一致;主库 mtime 09-26 02:00,备库 08-1410 min实锤失谐;时间点锁定
3变更回溯:安全平台轮换工单(02:00)∩ 值班 DEFER 窗口(01:30~04:30)35 min因果链闭合
4值班曾尝试在备库 orapwd 重建密码文件 → 仍 16191(原因见第 3 步)40 min走弯路,被证伪
5scp 主库 orapwPAYCORE 覆盖备库同名文件(保持属主/权限 640)2 min—
6主库 ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_2=ENABLE + 备库 ALTER DATABASE RECOVER MANAGED STANDBY DATABASE ... 重启 MRP6 min传输恢复,FAL 自动拉取 GAP
7监控 apply 速率约 29 MB/s,41 个序列追平1h47mlag 归零

排查过程

第 1 步:故障定界——主库还能不能救

# 主机层:存活,负载正常
uptime && multipath -ll | grep -i failed
# 存储链路层:8 条路径中 4 条 failed(控制器 B 路径全灭)

# 数据库层:实例死亡,alert log 末尾
tail -100 alert_PAYCORE.log | grep -E "error|terminated"

输出:故障域在存储控制器,DB 是受害者。转入"切换 or 修复"决策(见"应急响应与决策"一节)。

第 2 步:无 Broker 手工切换前置四件套

-- 备库 lag 三兄弟(无 Broker 下唯一的真相来源)
SELECT name, value, unit FROM v$dataguard_stats;
-- transport lag: 21:07:33   ← 爆了
-- apply lag:     21:07:31

-- GAP 清单
SELECT * FROM v$archive_gap ORDER BY thread#, low_sequence#;
-- 41 行:LOW_SEQUENCE# 4281 ~ HIGH_SEQUENCE# 4342

-- 传输目的地状态
SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;
-- STATUS=ERROR, ERROR='ORA-16191'   ← 凶手名字第一次出现

-- 备库打开模式
SELECT open_mode, database_role FROM v$database;
-- READ ONLY WITH APPLY / PHYSICAL STANDBY

输出:切换不可行。转修复主库;同时 ORA-16191 立项为第二故障单并行排查。

这四件套应该在每次切换前无条件执行。没有 Broker 时,v$dataguard_stats 和 v$archive_gap 是唯一能证明"备库可用"的证据;v$archive_dest 的 STATUS 只能说明当前状态,不能说明过去 5 天发生过什么。

第 3 步:ORA-16191 的认证链路

ORA-16191 认证链路

认证发生在备库的监听进程里:

LNS(主库) --TCP--> 备库监听 --> RFS 进程 fork
                                  |
                        用 SYS 口令做密码文件认证
                        (比对备库本地 orapwPAYCORE)
                                  |
                    主库 09-26 后的新散列 × 备库 08-14 的旧散列
                                  |
                        备库 alert: ORA-01017 → 回绝 → 主库 alert: ORA-16191

关键认知:认证用的是"备库本地的密码文件",口令本身不跨网络传输。所以修复方向只有一个:让备库文件内容与主库一致。与"再改一次密码""重启监听""重启 MRP"全部无关。

值班在备库用 orapwd 重建密码文件的尝试同样失败。原文没有记录失败的具体原因;从机制上说,密码文件认证比对的是文件内的 verifier,重建时必须与主库当前的 verifier 完全一致(含大小写敏感设置与条目集合),任何偏差都会继续 ORA-16191。因此可靠做法只有从主库整文件拷贝,而不是在备库重建。

第 4 步:双端取证——时间戳即供词

# 主库
ls -l $ORACLE_HOME/dbs/orapwPAYCORE
# -rw-r----- 1 oracle oinstall 7680 Sep 26 02:00   ← 安全平台轮换时刻

# 备库
ls -l $ORACLE_HOME/dbs/orapwPAYCORE
# -rw-r----- 1 oracle oinstall 7680 Aug 14 11:22   ← 上一次手工拷贝

md5sum orapwPAYCORE   # 双端不一致

两个 mtime 相差 43 天,正好把"上一次同步"和"这一次轮换"钉在时间轴上。

修复步骤

1. 修复主库存储(控制器更换 + 路径恢复),实例拉起,崩溃恢复完成,RPO=0。
2. 文件级拷贝密码文件:scp orapwPAYCORE paycore_stb:$ORACLE_HOME/dbs/(保持 640/oracle:oinstall),严禁在备库 orapwd 重建。
3. ENABLE DEST_2 + 重启 MRP,FAL 自动补 41 个 GAP 序列,1h47m 追平。
4. 追平后执行一次手工 switchover 演练验证(含反向传输认证),双向均通。

具体命令:

# 主库:确认目的地状态
sqlplus -s / as sysdba <<'EOF'
SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id=2;
EOF

# 主库:恢复传输
sqlplus -s / as sysdba <<'EOF'
ALTER SYSTEM SET LOG_ARCHIVE_DEST_STATE_2=ENABLE;
ALTER SYSTEM SWITCH LOGFILE;
EOF

# 备库:确认文件一致后重启 MRP
md5sum $ORACLE_HOME/dbs/orapwPAYCORE
sqlplus -s / as sysdba <<'EOF'
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
EOF

注意 LOG_ARCHIVE_DEST_STATE_2=ENABLE 只是打开目的地,不会自动补历史 GAP;补 GAP 依赖 FAL(Fetch Archive Log)机制,由备库向主库请求缺失序列。如果 v$archive_gap 长时间不收敛,再检查 FAL_SERVER / FAL_CLIENT 参数。

改进项

  1. 禁止在备份窗口 DEFER 传输。 这条 2023 年留下的 runbook 步骤,收益是"备份快照不抖动",代价是把容灾窗口变成了盲区。备份应使用 BACKUP ... NOT BACKUP 或独立通道,而不是停传输。
  2. apply lag 告警从 P4 提到 P1/P2。 备库只读不等于不紧急;lag 超过 30 分钟就应升级。
  3. 监控探针改为"数据在动"。 主机存活、端口通、MRP 进程存在,这三项全部通过也可能 5 天没传一条日志。应直接采集 v$dataguard_stats 的 apply lag 与 v$archive_dest 的 error。
  4. 口令轮换必须纳入 DG 变更清单。 12.2+ 的密码文件 redo 自动传输只在目的地 ENABLE 时有效;轮换前检查 DEST 状态,轮换后立即核对双端 md5sum。
  5. 每月密码文件同步改为每次口令变更后同步。 固定周期同步无法覆盖窗口外的变更。
  6. 季度做一次真实 switchover 演练。 演练要包含反向传输认证,否则只验证了单向。

待核对

  • 原文概览表记"Redo 传输中断时长 116 小时",但按同一行给出的 09-26 04:31 ~ 10-01 13:47 计算,实际为 129 小时 16 分。两者不一致,正文保留原始时间戳,未改写数字。
  • 原文概览表记"容灾失效窗口 4 天 21 小时",按 09-26 02:00 ~ 10-01 09:40 计算为 4 天 7 小时 40 分。差异原因不明,发布前请以监控原始数据核对。
  • "187 GB redo / 41 个序列 / 29 MB/s"来自原文与监控截图,实际值以 v$archived_log 汇总为准。
  • 概览表同时出现"CDB,2 PDB"与"非容器化部署",按上下文理解为"非 Docker/K8s 部署",不是 non-CDB;发布前请与原文作者确认。
  • 存储微码版本号原文未给出具体值,只说明"厂商公告过但未安排升级";对外发布前建议补型号与微码版本。

原文首发于微信公众号「止水研习社」。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: Data Guard Oracle 故障复盘
最后更新:2026-10-17

Aekor

这个人很懒,什么都没留下

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

使用AI教程

  • API报错解决方案
  • API 基础知识
  • API Key 获取
  • 最新接入教程

分类

  • Blog
  • Oracle 数据库
  • TradingAgents-CN
  • 使用教程
  • 数据库

COPYRIGHT © 2026 Aekor. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang