目录标题
WAIT_FOR_LOG的根因、是不是也可以failover、后续在主库异常关闭应该如何处理
结论先说
- WAIT_FOR_LOG 的本质:备库把当前序列用完了,在等下一段 redo(可能是主库还没产生日志、传输暂时断了、或主库已经被关机),不等于有 GAP。
- 能不能在 WAIT_FOR_LOG 下切换:可以,但要满足“无缺口(no gap)且可接受的 RPO”。若要 RPO=0,需满足同步传输 + 实时应用 + 无缺口;否则只能做 有损(IMMEDIATE) 的 failover。
- 主库异常关闭的处理:按“先判缺口 → 判RPO → 决策 failover/switchover”的流程执行,并对网络/参数与 SRL 做好预防加固。
1) WAIT_FOR_LOG 的常见根因(含你这次现场)
- 主库暂未产生下一序列(业务安静/在线日志未切换)。
- 主库被立刻关闭,备库应用到 S-68 后没有 S-69可用,于是进入 WAIT_FOR_LOG。
- 传输短暂中断:网络抖动、监听/服务名异常、
LOG_ARCHIVE_DEST_n错误、REOPEN/NET_TIMEOUT不当。 - 只启用“归档应用”而非“实时应用”(SRL 缺失或未在用),导致必须等到归档文件才继续。
- FRA/归档空间问题(主库归档受阻、备库接收受阻)。
和 WAIT_FOR_GAP 不同:WAIT_FOR_LOG 是“等下一段”,而 GAP 是“中间断层”。
2) WAIT_FOR_LOG 场景下能否 failover?
可以的情形
-
零或可忽略丢失(推荐)
满足全部或大部分:v$archive_gap为空;- 最近接收序列 = 最近已应用序列;
apply lag≈0;- 使用 SRL + Real-Time Apply;
- 传输为 SYNC AFFIRM(可实现 WITH NO DATA LOSS)。
可执行: - Broker:
FAILOVER TO <standby> WITH NO DATA LOSS - 手工:
RECOVER MANAGED STANDBY DATABASE FINISH;→ACTIVATE STANDBY DATABASE;
-
业务允许极小 RPO(有损)
无缺口但无法证明主库不会再产生日志,或传输恢复无望:- Broker:
FAILOVER TO <standby> IMMEDIATE - 手工:取消恢复 →
ACTIVATE STANDBY DATABASE;
可能丢主库最后尚未送达的一点 redo。
- Broker:
不建议的情形
- 出现/怀疑 GAP(
WAIT_FOR_GAP、v$archive_gap非空、RFS/传输报错)。 apply lag明显、接收与应用差距大。
→ 先补齐并应用,再切换。
快速判定 SQL(备库)
-- 1) 是否有缺口
SELECT * FROM v$archive_gap;
-- 2) 进程与序列
SELECT process,status,thread#,sequence#,block#,blocks
FROM v$managed_standby ORDER BY process;
-- 3) 最近接收/已应用到哪
SELECT thread#, MAX(CASE WHEN archived='YES' THEN sequence# END) last_received
FROM v$archived_log GROUP BY thread#;
SELECT thread#, MAX(CASE WHEN applied='YES' THEN sequence# END) last_applied
FROM v$archived_log GROUP BY thread#;
-- 4) 延迟
SELECT name,value FROM v$dataguard_stats
WHERE name IN ('transport lag','apply lag');
3) 主库异常关闭的标准处置流程(建议落到SOP)
A. 现场初判(1–3 分钟)
-
业务侧沟通:确认 RPO/RTO 目标(是否必须 0 丢失)。
-
备库检查(上面四组 SQL):
- 若 no gap + apply≈received → 可按 RPO 决策切换;
- 若有 gap/报错 → 先补日志或判断主库可否短时拉起生成/抽取日志。
B. 决策分支
-
目标 RPO=0(零丢失)
必须满足:SYNC AFFIRM + SRL + 实时应用 + 无缺口。- Broker:
FAILOVER ... WITH NO DATA LOSS - 手工:
FINISH→ACTIVATE
若条件不满足且业务仍要求 0 丢失 → 不可切换,应尝试拉起主库导出/注册缺失 redo(含在线日志)。
- Broker:
-
接受 RPO>0(有损)
- Broker:
FAILOVER ... IMMEDIATE - 手工:
CANCEL→ACTIVATE
之后按 C 节把旧主库收编为新备库。
- Broker:
C. 切换后的收敛(主库恢复/回补)
- 开启 Flashback 的理想路径:
旧主库恢复后 →FLASHBACK DATABASE TO SCN <新主库切换点>→ 转换为备库并加入 Data Guard。 - 无 Flashback:
用 RMAN DUPLICATE FROM ACTIVE 重建为备库。
4) 事前加固(强烈建议)
- SRL 充足且在用
-
每线程 组数 ≥ 在线日志组数,大小匹配;
-
备库启用实时应用:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT;
- 传输参数(主库 LOG_ARCHIVE_DEST_n)
- 如果追求 0 丢失:
SYNC AFFIRM NET_TIMEOUT=30 REOPEN=60 VALID_FOR=(ONLINE_LOGFILE,PRIMARY_ROLE) - 否则:
ASYNC NOAFFIRM+ 合理NET_TIMEOUT/REOPEN - 定期
ARCHIVE LOG CURRENT验证通道;监控v$archive_dest_status.error。
- 网络与 OS
-
TCP keepalive(示例):
net.ipv4.tcp_keepalive_time=60 net.ipv4.tcp_keepalive_intvl=10 net.ipv4.tcp_keepalive_probes=6 -
MTU/丢包/会话老化策略检查,备库监听与服务名一致可达。
- 容量与告警
- FRA 容量≥峰值两倍,监控 ORA-198xx;
- 清理/压缩归档(必要时启
LOG_ARCHIVE_DEST_n ... COMPRESSION=ENABLE)。
- 补丁一致性
- 定期跑
datapatch -verbose,清理你看到的DBMS_BACKUP_RESTORE/DBMS_RCVMAN版本不一致告警,避免 RMAN 任务异常。
5) 给你们 HA 前置校验的落地建议(伪代码)
if status == WAIT_FOR_GAP or v$archive_gap non-empty:
block_failover("GAP present")
elif status in (WAIT_FOR_LOG, APPLYING_LOG):
if last_received_seq == last_applied_seq and apply_lag≈0:
allow_failover(mode = NO_DATA_LOSS if SYNC+SRL else POLICY)
else:
allow_failover_only_if(RPO>0 explicitly accepted)
else:
block_or_require_manual_review()
小结
- 根因:WAIT_FOR_LOG 说明“缺下一段”,在你这次是主库被
shutdown immediate后不再产生日志导致。 - 是否可切:可,在 无缺口 且满足你的 RPO 前提下实施(满足 SYNC+SRL 时可 0 丢失;否则有损)。
- 以后怎么做:按 SOP 快速判定 → 按 RPO 决策 → 切换后收敛;同时从 SRL/传输参数/网络/FRA/补丁 五方面做预防加固。
最近接收序列 = 最近已应用序列; 这个是什么,要怎么判断
好问题!这两个“序列”都是指 主库 redo 日志的 sequence#,在备库侧各自代表:
- 最近接收序列(last_received_seq):备库已经收到并登记的最新一段归档日志(或由 RFS/FAL拉取)的最大
sequence#。 - 最近已应用序列(last_applied_seq):备库已经应用完成(Apply=YES)的最大
sequence#。
当 last_received_seq = last_applied_seq 且无 GAP 时,说明“收到了多少就应用到多少”,MRP0 进入 WAIT_FOR_LOG 纯属“等下一段”,这时按你的 RPO 策略可考虑切换。
一把梭的判定 SQL(在备库执行)
-- 1) 是否存在缺口(有值就先别切)
SELECT * FROM v$archive_gap;
-- 2) 最近接收 vs 最近已应用(多线程环境逐线程看)
WITH r AS (
SELECT thread#, MAX(sequence#) AS last_received_seq
FROM v$archived_log
WHERE registrar IN ('RFS','FAL') -- 备库收到的归档
GROUP BY thread#
),
a AS (
SELECT thread#, MAX(sequence#) AS last_applied_seq
FROM v$archived_log
WHERE applied = 'YES' -- 备库已完整应用的归档
GROUP BY thread#
)
SELECT COALESCE(r.thread#, a.thread#) AS thread#,
r.last_received_seq,
a.last_applied_seq,
(r.last_received_seq - a.last_applied_seq) AS seq_gap
FROM r FULL OUTER JOIN a USING(thread#)
ORDER BY 1;
解读:
seq_gap = 0且v$archive_gap为空 ⇒ “收多少用多少”。seq_gap > 0⇒ 还在追日志或卡住,先别切。
再配合进程与延迟快速看一眼:
-- 3) MRP0/RFS状态与当前正在应用的序列(实时把关)
SELECT process, status, thread#, sequence#, block#, blocks
FROM v$managed_standby
WHERE process IN ('MRP0','RFS')
ORDER BY process;
-- 4) 传输/应用延迟(近似判断是否贴近实时)
SELECT name, value
FROM v$dataguard_stats
WHERE name IN ('transport lag','apply lag','apply finish time');
备注(实时应用场景):如果启用了 Standby Redo Log + USING CURRENT LOGFILE,MRP0 可能在应用 SRL 中的当前序列,这时
last_applied_seq只统计已完整应用的归档,看起来会落后 1;结合v$managed_standby里MRP0 status=APPLYING_LOG/WAIT_FOR_LOG与apply lag≈0才能判断是否“基本追平”。
小结(如何用它决定能否切)
- 先看
v$archive_gap:有缺口就补,没缺口再往下。 - 看上面的对比查询:所有线程
seq_gap=0⇒ “收多少用多少”。 - 看
v$managed_standby+v$dataguard_stats:WAIT_FOR_LOG或APPLYING_LOG且apply lag≈0⇒ 在你接受的 RPO 下即可切换;若要求 RPO=0,需再满足 SYNC+SRL 等条件。
预警日志的内容可以作证吗?
这三行正好能证明当时“最近接收序列 = 最近已应用序列 = 68”,属于纯粹在“等下一段(69)”的 WAIT_FOR_LOG 场景。
怎么从日志看出来:
14:00:15 Recovery of Online Redo Log ... Seq 68→ 备库正在应用 Seq 68(实时应用 SRL/在线日志)。14:07:47 ARC1 ... Archived Log entry ... S-68→ 备库已把 Seq 68 完整归档到本地(说明已完整接收且已应用完该序列)。- 紧接着(你之前贴过)
PR00: Media Recovery Waiting for T-1.S-69→ 应用完 68,开始等 69。
=> 因此在 14:07:47 时刻:
last_received_seq = 68(已归档到本地)
last_applied_seq = 68(应用完并等待 69)
对切换的含义:
- 若主库已关机、不再产生 69,且确认无 GAP,这是可在
WAIT_FOR_LOG下切换的典型“收多少用多少”状态。 - 需做到零丢失时,还要满足 SYNC + SRL 实时应用 等前提;否则按策略接受极小 RPO(IMMEDIATE)即可。
严谨起见,可在备库再跑一遍校验:
SELECT * FROM v$archive_gap; -- 应为空
WITH r AS (
SELECT thread#, MAX(sequence#) last_received_seq
FROM v$archived_log WHERE registrar IN ('RFS','FAL') GROUP BY thread#
),
a AS (
SELECT thread#, MAX(sequence#) last_applied_seq
FROM v$archived_log WHERE applied='YES' GROUP BY thread#
)
SELECT COALESCE(r.thread#,a.thread#) thread#, r.last_received_seq, a.last_applied_seq
FROM r FULL OUTER JOIN a USING(thread#);
看到线程1里两个值同为 68(且无 GAP),就与这段日志结论一致,可以据此决定是否发起 failover。
1万+

被折叠的 条评论
为什么被折叠?



