暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

Every Day of a DBA,第154期: Oracle 19c Active Data Guard — Automatic Block Media Recovery (ABMR)

原创 ByteHouse 2天前
22

1. 文档目的

ABMR 仅修复物理坏块(Physical Block Corruption),绝不修复逻辑坏块(Logical Block Corruption)。

本文档涵盖:

  • ABMR 工作原理与架构
  • 生效的硬性前提条件(四条件缺一不可)
  • 物理坏块 vs 逻辑坏块的本质区别
  • ABMR 触发验证方法(而非"凭感觉以为在工作")
  • 逻辑坏块在备库上的正确修复路径
  • 常见误区和故障排除

2. Automatic Block Media Recovery (ABMR)

ABMR 是 Oracle Active Data Guard 提供的一项自动块级故障恢复机制。当主库或备库在运行时遇到 物理坏块(物理 I/O 读取失败,块校验和不匹配),ABMR 会自动从另一端的数据库(备库或主库)获取该块的完好副本,透明地覆盖损坏块,无需人工干预。

核心工作流程

┌─────────────────────────────────────────┐ │ 客户端查询触发物理块 I/O 失败 │ │ ORA-01578: 文件 X 块 Y 读取失败 │ └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Oracle 内核检测到校验和错误或块格式错误 │ │ 校验: 块是否属于可自动修复的物理坏块 │ │ ✗ 逻辑坏块 → 不触发 ABMR,直接报错 │ │ ✓ 物理坏块 → 进入 ABMR 流程 │ └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ ABMR 通过网络从另一端数据库请求块的副本 │ │ │ │ 备库出错 → 从主库 FETCH 干净块 │ │ 主库出错 → 从备库 FETCH 干净块 │ └─────────────────┬───────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 接收到块副本后, 写入损坏位置, 刷新缓存 │ │ 记录日志: │ │ "Automatic block media recovery: │ │ successfully restored block Y from │ │ <source>" │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 客户端重新尝试读取 — 成功 │ │ 整个流程对应用完全透明 │ └─────────────────────────────────────────┘

3. ABMR 生效的四个硬性前提条件

以下条件 必须同时满足,缺一不可。任何一个不满足,ABMR 都不会触发,且不会报明显错误(仅告警日志中记录静默跳过)。

条件 1:备库 OPEN_MODE 必须为 READ ONLY WITH APPLY

SELECT OPEN_MODE, DATABASE_ROLE FROM V$DATABASE;
正确值 错误值
READ ONLY WITH APPLY READ ONLY(无 Real-Time Apply)
MOUNTED(备库未打开)
READ WRITE(这在备库不可能)

⚠️ 常犯错误:仅设置了 READ ONLY 而没有 WITH APPLY,MRP 进程不运行,ABMR 初始化直接失败。

条件 2:MRP0 进程必须处于 APPLYING_LOG 状态

SELECT PROCESS, STATUS, CLIENT_PID, THREAD#, SEQUENCE# FROM V$MANAGED_STANDBY WHERE PROCESS LIKE 'MRP%';
正确状态 错误状态
APPLYING_LOG IDLE(Redo 应用暂停)
WAITING_FOR_LOG(无可用归档)
进程不存在(MRP 未启动)

条件 3:Active Data Guard 许可必须为 TRUE

SELECT * FROM V$OPTION WHERE PARAMETER = 'Active Data Guard';
正确值 错误值
TRUE FALSE(无 ADG 许可,ABMR 永远不工作)

这是 最容易被忽略的硬性条件。即使备库在运行,很多站点仅使用了 Oracle Data Guard(免费许可)而非 Active Data Guard(需要额外购买许可),ABMR 功能在内核层被禁用。

条件 4:LOG_ARCHIVE_CONFIG 必须在主备两端都包含完整的 DG 名称

-- 主库检查 SHOW PARAMETER log_archive_config; -- 备库检查 SHOW PARAMETER log_archive_config;

正确示例:

log_archive_config = 'DG_CONFIG=(orcl,orcl_stby)'
--                      ↑ 主库 DB_UNIQUE_NAME  ↑ 备库 DB_UNIQUE_NAME

错误示例:

log_archive_config = 'DG_CONFIG=(orcl)'            -- 漏了备库名称
log_archive_config = 'DG_CONFIG=(orcl_stby)'       -- 漏了主库名称
log_archive_config = ''                             -- 空值

如果主备任意一端漏掉了对方的 DB_UNIQUE_NAME,ABMR 的跨库块级拉取通道在初始化时就会失败。


4. 物理坏块 vs 逻辑坏块 — 本质区别

这是 ABMR 主题下 最核心、最容易被误解 的概念。

4.1 物理坏块(Physical Corruption)

特征 说明
定义 块在存储介质层面已损坏(磁盘扇区损坏、RAID 校验失败、存储缓存故障)
检测方式 Oracle 读取块后,计算校验和与块头中记录的校验和不匹配
表现 ORA-01578: ORACLE data block corrupted (file # X, block # Y)
V$DATABASE_BLOCK_CORRUPTION CORRUPTION_TYPE = 'FRACTURED''CHECKSUM'
根源 存储层问题,数据库内部结构本身无问题
ABMR 是否工作 — 从另一端拷贝完好副本即可修复

4.2 逻辑坏块(Logical Corruption)

特征 说明
定义 块校验和与头格式都正确,但块内部数据逻辑结构已损坏(如索引条目指向不存在的数据行、事务槽(ITL)损坏、行目录溢出)
检测方式 ANALYZE TABLE ... VALIDATE STRUCTURE CASCADERMAN VALIDATE CHECK LOGICALDBVERIFYlogonly=yes 参数
表现 ORA-01498: block check failure、查询返回不一致数据、索引查询报错
V$DATABASE_BLOCK_CORRUPTION CORRUPTION_TYPE = 'LOGICAL''MEDIUM'
根源 Oracle 内部逻辑损坏(Bug、异常关闭、介质恢复时 Redo 损坏、人为操作)
ABMR 是否工作 — 备库上的同一块也可能逻辑损坏(如果损坏来自 Redo 重放)

4.3 对比总结

比较维度 物理坏块 逻辑坏块
校验和 ❌ 不匹配 ✅ 匹配
块头格式 ❌ 错误或不可读 ✅ 正确
内部数据逻辑 未知 ❌ 不一致/损坏
ABMR 能否修复 ✅ 能 ❌ 不能
需要 RMAN 备份 不需要(利用对端副本) 需要
典型根因 磁盘坏道、RAID 故障、SSD 异常 Oracle Bug、异常关闭、Redo 损坏

5. 逻辑坏块为什么无法触发 ABMR — 深入解析

5.1 根因:逻辑损坏可能在备库上同样存在

ABMR 的核心假设是:另一端数据库的同一数据块是完好的

  • 主库物理坏块 → 备库完好 ✅ 成立(备库通过 Redo 重放获得了正确副本)
  • 备库物理坏块 → 主库完好 ✅ 成立(主库是原始数据来源)
  • 备库逻辑坏块 → 主库可能也坏 ❌ 不成立

为什么?因为逻辑坏块的成因可能是:

  1. Redo 日志损坏:主库写入坏 Redo,传到备库重放后,备库也产生同样的逻辑损坏
  2. Oracle Bug 触发的逻辑损坏:主库某版本 Bug 导致段内逻辑结构损坏,Redo 记录的是损坏后的状态,备库重放后也损坏
  3. 备库自身异常:备库文件系统异常、存储缓存损坏,导致 Redo 应用到备库后产生逻辑损坏(但主库完好)

在上述场景 #1 和 #2 中,即使 ABMR 从主库拉取块副本,拿到的也是 同样损坏的逻辑块,修复无意义。

5.2 ABMR 的防御机制

Oracle 内核在决定是否触发 ABMR 时,会判断坏块的类型:

  • 如果块校验和错误 → 物理坏块 → 发起 ABMR
  • 如果块校验和正确但逻辑检查失败 → 逻辑坏块 → 跳过 ABMR,直接报错返回给客户端
-- 通过告警日志验证 ABMR 是否尝试触发 -- 逻辑坏块:仅看到 ORA-01578,无 ABMR 相关日志 -- 物理坏块:ORA-01578 后紧跟 "Automatic block media recovery: requesting block..."

6. 验证 ABMR 是否真正在工作(而不是你以为它在工作)

6.1 通过告警日志验证

这是 最硬、最可靠 的验证方法。不要只看参数设置,要实际模拟故障并观察日志。

-- 查看主库告警日志路径 SELECT VALUE FROM V$DIAG_INFO WHERE NAME = 'Diag Alert';
# tail 实时跟踪 tail -f $ORACLE_BASE/diag/rdbms/<db_name>/trace/alert_<sid>.log | grep -i "automatic block media recovery"

看到以下输出 = ABMR 真正在工作:

Automatic block media recovery: requesting block 1234 from standby for file 5
Automatic block media recovery: successfully restored block 1234 from standby for file 5

只看到以下内容 = ABMR 没有触发(逻辑坏块或条件不满足):

ORA-01578: ORACLE data block corrupted (file # 5, block # 1234)
ORA-01110: data file 5: '/u01/oradata/users01.dbf'

后面没有 ABMR 恢复日志。

6.2 模拟物理坏块验证(测试环境)

⚠️ 仅可在测试环境执行!不得在生产环境执行!

# 1. 确定数据文件路径和块号 # 从 V$DATABASE_BLOCK_CORRUPTION 或: SELECT file_name, file_id FROM DBA_DATA_FILES WHERE tablespace_name = 'USERS'; # 2. 在主库使用 dd 损坏一个物理块 # 假设 file_id=5, block_number=12345 # 块大小 8192 字节 dd if=/dev/zero of=/u01/oradata/users01.dbf bs=8192 conv=notrunc seek=12344 count=1 # ↑ seek = block_number - 1(dd 的 seek 是 0-based) # 3. 立即查询该表 SELECT COUNT(*) FROM test_table; -- 预期:ORA-01578 # 4. 检查告警日志 tail -f $ORACLE_BASE/diag/rdbms/<db_name>/trace/alert_*.log | grep -i "automatic block" # 5. 如果 ABMR 正常工作,会看到请求/恢复日志 # 验证坏块是否消失 SELECT * FROM V$DATABASE_BLOCK_CORRUPTION WHERE FILE# = 5 AND BLOCK# = 12345; -- 应该无记录(已自动修复)

6.3 通过 V$DATABASE_BLOCK_CORRUPTION 区分坏块类型

SELECT FILE#, BLOCK#, BLOCKS, CORRUPTION_TYPE, CORRUPTION_CHANGE#, RAC_ID FROM V$DATABASE_BLOCK_CORRUPTION;
CORRUPTION_TYPE 含义 ABMR 可修复?
FRACTURED 块内容断裂(物理)
CHECKSUM 校验和不匹配(物理)
CORRUPT 通用损坏(可能是物理或逻辑) ❓ 需进一步分析
LOGICAL 逻辑一致性损坏
MEDIUM 介质损坏但校验和正确(逻辑)

7. 逻辑坏块在备库上的正确处理路径

一旦 V$DATABASE_BLOCK_CORRUPTION 中确认损块类型为 LOGICALMEDIUMABMR 路径立即失效,应切换到以下方案之一:

7.1 方案 A:RMAN BLOCKRECOVER(推荐,当有可用备份时)

# 在备库执行 BLOCKRECOVER(修复备库本地坏块) RMAN> BLOCKRECOVER DATAFILE 5 BLOCK 12345;

工作原理

  • RMAN 从备份(自动或手动指定的备份集)中定位该块备份
  • 若使用存档日志需要恢复到块版本一致性
  • 覆盖备库上的损坏块

优点:不需要主库参与,不依赖主库块的完好性。

限制

  • 需要备库有完整的、未被损坏的 RMAN 备份
  • 无法对 SYSTEM 表空间或 UNDO 表空间的块执行(RMAN 限制)
  • 备份必须是在该逻辑损坏发生之前创建的

7.2 方案 B:DBMS_REPAIR(无备份,非关键数据)

-- 1. 创建 Repair 表 BEGIN DBMS_REPAIR.ADMIN_TABLES( table_name => 'REPAIR_TABLE', table_type => DBMS_REPAIR.REPAIR_TABLE, action => DBMS_REPAIR.CREATE_ACTION, tablespace_name => 'USERS' ); END; / -- 2. 检查坏块 DECLARE v_num_corrupt INT; BEGIN v_num_corrupt := DBMS_REPAIR.CHECK_OBJECT( schema_name => 'SCOTT', object_name => 'EMP', repair_table => 'REPAIR_TABLE' ); DBMS_OUTPUT.PUT_LINE('Corrupt blocks found: ' || v_num_corrupt); END; / -- 3. 标记块为跳过(丢失该块数据,慎用!) DECLARE v_num_corrupt INT; BEGIN v_num_corrupt := DBMS_REPAIR.FIX_CORRUPT_BLOCKS( schema_name => 'SCOTT', object_name => 'EMP', repair_table => 'REPAIR_TABLE' ); DBMS_OUTPUT.PUT_LINE('Blocks fixed: ' || v_num_corrupt); END; / -- 4. 跳过损坏块读取数据 SELECT /*+ SKIP_CORRUPT */ * FROM SCOTT.EMP;

⚠️ DBMS_REPAIR 是在丢失数据的前提下跳过坏块,执行前务必评估数据可丢失性。建议先抢救数据再标记。

7.3 方案 C:索引损坏 — 直接重建

如果逻辑坏块发生在 索引段(最常见的情况):

-- 通过 ANALYZE 或 V$DATABASE_BLOCK_CORRUPTION 确认 SELECT SEGMENT_NAME, SEGMENT_TYPE, PARTITION_NAME, TABLESPACE_NAME FROM DBA_EXTENTS WHERE FILE_ID = 5 AND 12345 BETWEEN BLOCK_ID AND (BLOCK_ID + BLOCKS - 1); -- 如果段类型为 INDEX DROP INDEX <owner>.<index_name>; CREATE INDEX <owner>.<index_name> ON <table>(<columns>) ...;

不需要修复坏块本身 — 删除重建索引即可得到无损坏的新索引结构。

7.4 方案 D:通过 CTAS 抢救表数据

对于表段逻辑损坏:

-- 1. 使用 ROWID 分段扫描定位可读记录 -- 先找出坏块对应的 ROWID 范围 -- 2. 跳过坏块 CREATE TABLE AS SELECT CREATE TABLE scott.emp_good AS SELECT /*+ SKIP_CORRUPT */ * FROM scott.emp; -- 3. 验证行数 SELECT COUNT(*) FROM scott.emp_good; SELECT COUNT(*) FROM scott.emp; -- 可能比上面多或少(如果有坏块被跳过) -- 4. 确认数据完整性后,替换原表 DROP TABLE scott.emp; ALTER TABLE scott.emp_good RENAME TO emp;

7.5 方案对比

方案 适用场景 数据丢失率 是否需要 RMAN 备份 复杂度
RMAN BLOCKRECOVER 有可用备份 0% ✅ 需要
DBMS_REPAIR 无备份,非核心表 部分丢失(坏块内数据) ❌ 不需要
索引重建 索引逻辑损坏 0%(数据在表中) ❌ 不需要
CTAS 抢救 表段损坏 坏块内行丢失 ❌ 不需要
从主库重建备库 大面积逻辑损坏 取决于恢复点 ❌ 不需要

8. 常见误区和 FAQ

误区 1:ABMR 可以修复任何坏块

事实:ABMR 仅修复物理坏块。逻辑坏块(LOGICAL / MEDIUM 类型)永远不会触发 ABMR。

误区 2:只要启用了 Data Guard 就有 ABMR

事实:ABMR 需要 Active Data Guard 许可V$OPTION 返回 TRUE)。仅使用免费 Data Guard(物理备库处于 MOUNT 状态)不支持 ABMR。

误区 3:备库 OPEN_MODE = READ ONLY 就够了

事实:必须是 READ ONLY WITH APPLY。缺了 WITH APPLY 表示没有 Real-Time Apply,MRP 进程不运行,ABMR 初始化失败。

误区 4:只要设置了参数,ABMR 就会自动工作

事实:四个条件缺一不可。最常见的问题是 LOG_ARCHIVE_CONFIG 中漏掉了一端的数据库唯一名称。

误区 5:逻辑坏块可以通过从主库拉取块来解决

事实:逻辑坏块可能是因为 Redo 损坏或 Oracle Bug 导致,主库上的同一块也可能已损坏,或备库重放 Redo 后同样损坏。ABMR 不尝试逻辑坏块修复。


9. 主动预防与监控建议

9.1 定期检测坏块

-- RMAN 物理与逻辑检测 RMAN> BACKUP VALIDATE CHECK LOGICAL DATABASE; -- 或 RMAN> VALIDATE CHECK LOGICAL DATABASE; -- DBVERIFY $ dbv file=/u01/oradata/users01.dbf blocksize=8192 logonly=yes

9.2 监控 ABMR 活动

-- 查看 ABMR 相关的统计信息 SELECT NAME, VALUE FROM V$SYSSTAT WHERE NAME LIKE '%automatic block%'; -- Active Data Guard 统计 SELECT NAME, VALUE, TIME_COMPUTED FROM V$ADG_STATS WHERE NAME LIKE '%block%';

9.3 确保 ABMR 条件持续满足

建议加入日常巡检脚本:

-- ABMR 条件巡检脚本 COLUMN condition FORMAT A50 COLUMN status FORMAT A20 SELECT 'Open Mode' AS condition, CASE WHEN OPEN_MODE = 'READ ONLY WITH APPLY' THEN '✅ PASS' ELSE '❌ FAIL' END AS status FROM V$DATABASE UNION ALL SELECT 'MRP0 Process', CASE WHEN EXISTS (SELECT 1 FROM V$MANAGED_STANDBY WHERE PROCESS = 'MRP0' AND STATUS = 'APPLYING_LOG') THEN '✅ PASS' ELSE '❌ FAIL' END FROM DUAL UNION ALL SELECT 'ADG License', CASE WHEN (SELECT VALUE FROM V$OPTION WHERE PARAMETER = 'Active Data Guard') = 'TRUE' THEN '✅ PASS' ELSE '❌ FAIL' END FROM DUAL UNION ALL SELECT 'LOG_ARCHIVE_CONFIG', CASE WHEN (SELECT VALUE FROM V$PARAMETER WHERE NAME = 'log_archive_config') LIKE '%DG_CONFIG=%' THEN '✅ CHECK (manual verify both sides)' ELSE '❌ FAIL' END FROM DUAL;
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论