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 CASCADE、RMAN VALIDATE CHECK LOGICAL、DBVERIFY 加 logonly=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 重放获得了正确副本)
- 备库物理坏块 → 主库完好 ✅ 成立(主库是原始数据来源)
- 备库逻辑坏块 → 主库可能也坏 ❌ 不成立
为什么?因为逻辑坏块的成因可能是:
- Redo 日志损坏:主库写入坏 Redo,传到备库重放后,备库也产生同样的逻辑损坏
- Oracle Bug 触发的逻辑损坏:主库某版本 Bug 导致段内逻辑结构损坏,Redo 记录的是损坏后的状态,备库重放后也损坏
- 备库自身异常:备库文件系统异常、存储缓存损坏,导致 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 中确认损块类型为 LOGICAL 或 MEDIUM,ABMR 路径立即失效,应切换到以下方案之一:
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;




