问题:
Oracle 数据文件出现坏块(典型报错 ORA-01578 数据块损坏、ORA-19566 坏块数超过 MAXCORRUPT 限制),导致 RMAN 备份无法完成、备份任务失败,需要既处理坏块又让备份能够跑完。
方案:
场景一:先定位并确认坏块(所有处理的起点)
不要直接动手修,先把「坏块在哪个文件、哪个块、属于哪个对象、是物理坏块还是内存坏块」确认清楚。
- 从 alert 日志拿第一手线索:搜索
Bad header found during backing up datafile、Corrupt block relative dba、Corrupt Block Found等关键字,记录文件号与块号。-- 查看 alert 日志(11g 及以后)-- adrci> show alert -tail 100 - 用 RMAN 校验(不实际备份,只扫描):
RMAN> BACKUP VALIDATE CHECK LOGICAL DATABASE; -- 或只校验单个数据文件 RMAN> BACKUP VALIDATE CHECK LOGICAL DATAFILE 5; RMAN> VALIDATE DATAFILE 5 BLOCK 131; - 查坏块登记视图:
SELECTfile#, block#, blocks, corruption_type, object# FROM V$DATABASE_BLOCK_CORRUPTION; - 用 dbv 做文件级物理扫描(可在库外执行):
dbv file=/oradata/xxx/tbs_test01.dbf⚠️ 注意:dbv 输出的文件编号可能不准确,不能直接把 dbv 的编号用于 BLOCKRECOVER,需通过
VALIDATE日志或V$DATAFILE核对真实文件号:SELECTfile#, name, status FROM V$DATAFILE; - 把坏块落到对象上(判断影响面):
SELECTo.owner,o.object_name,o.object_type,o.subobject_nameFROMdba_objects oWHEREo.data_object_id=&object_id; - 排除内存坏块:若工具扫描未发现坏块、但查询仍报错,可能是缓冲区内的内存坏块,可先刷新缓冲区缓存再复测:
ALTERSYSTEM FLUSH BUFFER_CACHE;
场景二:按对象类型处理坏块
1)索引坏块 → 直接重建
ALTERINDEX索引所有者.索引名 REBUILD;-- 或指定表空间重建ALTERINDEX索引所有者.索引名 REBUILDTABLESPACE目标表空间;2)表坏块且存在可用 RMAN 备份 → 块介质恢复(Block Media Recovery,优先方案)
只恢复受损数据块,无需恢复整个数据文件,不必将数据文件或表空间离线,停机影响最小。
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等阶段。 - 若损坏范围较大或块介质恢复失败,可退化为整文件恢复:
RMAN> RESTORE DATAFILE 5; RMAN> RECOVER DATAFILE 5;
3)自动块介质恢复(ABMR)
Oracle 支持在查询/访问到坏块时自动触发块介质恢复;也可通过SELECT触发验证修复效果。修复后可再次VALIDATE确认Marked Corrupt为 0。
4)表坏块但无可用备份 → DBMS_REPAIR 标记跳过(会丢数据,谨慎评估)
适用于物理坏块无法用备份修复、且业务能接受少量数据丢失的场景。
-- 1) 建立修复表EXECDBMS_REPAIR.ADMIN_TABLES('REPAIR_TABLE',1,'USERS');EXECDBMS_REPAIR.ADMIN_TABLES('ORPHAN_TABLE',2,'USERS');-- 2) 扫描对象,登记坏块DECLAREv_num NUMBER;BEGINv_num :=DBMS_REPAIR.CHECK_OBJECT(schema_name=>'SCOTT',object_name=>'T1',repair_table_name=>'REPAIR_TABLE');END;/-- 3) 标记坏块DECLAREv_num NUMBER;BEGINv_num :=DBMS_REPAIR.FIX_CORRUPT_BLOCKS(schema_name=>'SCOTT',object_name=>'T1',fix_count=>NULL,repair_table_name=>'REPAIR_TABLE');END;/-- 4) 设置跳过坏块,使对象可访问DECLAREv_num NUMBER;BEGINv_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,让备份跳过坏块先完成
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)验证坏块是否真正消除
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/介质问题引起,修复后应检查存储链路,避免复发。