问题:
Oracle 数据文件出现坏块(典型报错 ORA-01578 数据块损坏、ORA-19566 坏块数超过 MAXCORRUPT 限制),导致 RMAN 备份无法完成、备份任务失败,需要既处理坏块又让备份能够跑完。
方案:
场景一:先定位并确认坏块(所有处理的起点)
不要直接动手修,先把「坏块在哪个文件、哪个块、属于哪个对象、是物理坏块还是内存坏块」确认清楚。
-
从 alert 日志拿第一手线索 :搜索
Bad header found during backing up datafile、Corrupt block relative dba、Corrupt Block Found等关键字,记录文件号与块号。sql-- 查看 alert 日志(11g 及以后) -- adrci> show alert -tail 100 -
用 RMAN 校验 (不实际备份,只扫描):
textRMAN> BACKUP VALIDATE CHECK LOGICAL DATABASE; -- 或只校验单个数据文件 RMAN> BACKUP VALIDATE CHECK LOGICAL DATAFILE 5; RMAN> VALIDATE DATAFILE 5 BLOCK 131; -
查坏块登记视图 :
sqlSELECT file#, block#, blocks, corruption_type, object# FROM V$DATABASE_BLOCK_CORRUPTION; -
用 dbv 做文件级物理扫描 (可在库外执行):
textdbv file=/oradata/xxx/tbs_test01.dbf⚠️ 注意:dbv 输出的文件编号可能不准确,不能直接把 dbv 的编号用于 BLOCKRECOVER ,需通过
VALIDATE日志或V$DATAFILE核对真实文件号:sqlSELECT file#, name, status FROM V$DATAFILE; -
把坏块落到对象上 (判断影响面):
sqlSELECT o.owner, o.object_name, o.object_type, o.subobject_name FROM dba_objects o WHERE o.data_object_id = &object_id; -
排除内存坏块 :若工具扫描未发现坏块、但查询仍报错,可能是缓冲区内的内存坏块,可先刷新缓冲区缓存再复测:
sqlALTER SYSTEM FLUSH BUFFER_CACHE;
场景二:按对象类型处理坏块
1)索引坏块 → 直接重建
sql
ALTER INDEX 索引所有者.索引名 REBUILD;
-- 或指定表空间重建
ALTER INDEX 索引所有者.索引名 REBUILD TABLESPACE 目标表空间;
2)表坏块且存在可用 RMAN 备份 → 块介质恢复(Block Media Recovery,优先方案)
只恢复受损数据块,无需恢复整个数据文件,不必将数据文件或表空间离线,停机影响最小。
text
RMAN> BLOCKRECOVER DATAFILE 5 BLOCK 131;
-- 恢复 V$DATABASE_BLOCK_CORRUPTION 中登记的全部坏块
RMAN> BLOCKRECOVER CORRUPTION LIST;
-- 限定从某时间点之前的备份恢复
RMAN> BLOCKRECOVER CORRUPTION LIST RESTORE UNTIL TIME 'SYSDATE - 7';
-
前提条件:存在有效的全量备份 + 归档日志,且备份中对应块本身未损坏。
-
恢复过程会输出
restoring block(s)→reading from backup piece→media recovery complete等阶段。 -
若损坏范围较大或块介质恢复失败,可退化为整文件恢复:
textRMAN> RESTORE DATAFILE 5; RMAN> RECOVER DATAFILE 5;
3)自动块介质恢复(ABMR)
Oracle 支持在查询/访问到坏块时自动触发块介质恢复;也可通过 SELECT 触发验证修复效果。修复后可再次 VALIDATE 确认 Marked Corrupt 为 0。
4)表坏块但无可用备份 → DBMS_REPAIR 标记跳过(会丢数据,谨慎评估)
适用于物理坏块无法用备份修复、且业务能接受少量数据丢失的场景。
sql
-- 1) 建立修复表
EXEC DBMS_REPAIR.ADMIN_TABLES('REPAIR_TABLE', 1, 'USERS');
EXEC DBMS_REPAIR.ADMIN_TABLES('ORPHAN_TABLE', 2, 'USERS');
-- 2) 扫描对象,登记坏块
DECLARE
v_num NUMBER;
BEGIN
v_num := DBMS_REPAIR.CHECK_OBJECT(
schema_name => 'SCOTT',
object_name => 'T1',
repair_table_name => 'REPAIR_TABLE');
END;
/
-- 3) 标记坏块
DECLARE
v_num NUMBER;
BEGIN
v_num := DBMS_REPAIR.FIX_CORRUPT_BLOCKS(
schema_name => 'SCOTT',
object_name => 'T1',
fix_count => NULL,
repair_table_name => 'REPAIR_TABLE');
END;
/
-- 4) 设置跳过坏块,使对象可访问
DECLARE
v_num NUMBER;
BEGIN
v_num := DBMS_REPAIR.SKIP_CORRUPT_BLOCKS(
schema_name => 'SCOTT',
object_name => 'T1');
END;
/
- 边界:DBMS_REPAIR 只处理事务层/数据层的软件损坏 ,对物理损坏块的修复能力有限;标记跳过后被跳过块的数据会丢失(典型案例:1000 行的表查询后只剩 917 行,丢失 83 行)。
- 建议:跳过只是让对象「能用」,最终应把好数据导出后重建表。
5)表坏块 → 提取好数据后重建表
通过 ROWID 分段扫描,把未损坏的数据导出到新表,再改名替换;注意可能产生重复行,需配合去重逻辑。
6)未使用的空块 → 可忽略(不影响对象访问)。
场景三:备份遇到坏块(ORA-19566),如何把备份跑完
现象:RMAN 报 ORA-19566: exceeded limit of 0 corrupt blocks,备份终止。根因是数据文件中有坏块,而 RMAN 默认 MAXCORRUPT = 0(零容忍)。
处理优先级:先尽量修复坏块,再备份;确实无法修复时才放宽容忍度。
1)首选:先做块介质恢复,再正常备份
按场景二用 BLOCKRECOVER / ABMR 修复坏块,修复后重新执行备份即可。
2)次选:临时放宽 MAXCORRUPT,让备份跳过坏块先完成
text
RMAN> run {
SET MAXCORRUPT FOR DATAFILE 5 TO 100;
BACKUP DATABASE;
}
- 含义:允许数据文件 5 在本次备份中最多忽略 100 个坏块,使备份能够完成。
- ⚠️ 必须写在
run { }块内 ,直接执行SET MAXCORRUPT ...会报RMAN-03031: this option of set command needs to be used inside a run block。 - 这只是「让备份先跑完」,坏块并未修复;备份完成后仍需按场景二修复坏块,并再次备份验证。
3)验证坏块是否真正消除
text
RMAN> BACKUP VALIDATE CHECK LOGICAL DATAFILE 5;
确认输出中 Marked Corrupt = 0、各 Block Type 的 Failing Blocks 均为 0。
场景四:兜底与影响面控制
- 确认备份可用的前提:块介质恢复依赖有效的全量 + 归档备份;若备份链断裂,只能走 DBMS_REPAIR / 数据提取重建路线。
- 对象级优先 :能用备份恢复的优先
BLOCKRECOVER;索引坏块优先REBUILD;表坏块在无备份时才考虑 DBMS_REPAIR。 - 数据一致性风险:DBMS_REPAIR 跳过、Extract 提取都可能造成丢行或重复行,处理前后务必核对行数与关键数据。
- 根因排查:坏块多由存储/IO/介质问题引起,修复后应检查存储链路,避免复发。