应优先用ROWID分段扫描提取数据,而非直接DBMS_REPAIR;因DBA_SEGMENTS可查但表访问报ORA-01578,表明属段级物理坏块,需通过ROWID范围+FULL提示跳过坏块逐片抽取,EXP支持query参数行级过滤而EXPDP不支持。

直接跳过坏块读取段数据,DBMS_REPAIR 不是首选
当 DBA_EXTENTS 或 DBA_SEGMENTS 仍可查询,但访问某张表报 ORA-01578(ORACLE data block corrupted)时,说明损坏局限在某个段的物理块上,而非整个文件不可识别。此时不建议先用 DBMS_REPAIR 标记坏块——它只解决“后续访问绕过”,不帮你把已存在的数据捞出来。真正要做的,是尽快以只读方式从受损段中提取可用行。
关键判断点:SELECT COUNT(*) FROM schema.table 报错,但 SELECT segment_name, blocks FROM dba_segments WHERE owner='SCHEMA' AND segment_name='TABLE' 能返回结果,就基本确认是段级损坏而非数据字典级崩溃。
用 ROWID 范围扫描跳过坏块区域
Oracle 的 ROWID 编码隐含了文件号、块号和行号。只要损坏不是连续大片(比如一整个 extent 全坏),就可以按块号切片,逐段 SELECT ... WHERE ROWID BETWEEN 扫描。
- 先查出该表所在文件号和起始块号:
SELECT header_file, header_block FROM dba_segments WHERE segment_name = 'TBL_NAME' - 用
DBMS_ROWID.ROWID_CREATE构造起始和结束ROWID,例如从块 1000 扫到 1099:WHERE ROWID BETWEEN CHARTOROWID('AAAE8gAABAAAMc8AAA') AND CHARTOROWID('AAAE8gAABAAAMc8ZZZ') - 每次扫描前加
/*+ FULL(t) */提示,避免走索引触发坏块 - 遇到报错立即中断当前范围,切到下一组块继续——不要依赖
EXCEPTION块捕获,PL/SQL 中坏块错误无法被常规异常处理拦截
EXP 和 EXPDP 在段损坏时的行为差异
EXP(传统导出)默认会尝试读取每个块,遇到坏块直接失败退出;EXPDP(Data Pump)在 CONTENT=DATA_ONLY 模式下,默认跳过无法读取的对象,但不会跳过段内部分坏块——它要么全导出,要么整表失败。
真正可用的组合是:exp userid=/ file=tbl.dmp tables=schema.table query=\"where rowid between '...' and '...'\”。注意:query 参数只在 EXP 中有效,EXPDP 不支持带 WHERE 的行级过滤导出。
如果表有 LOB 字段,且 LOB 段也损坏,EXP 会因 LOB 定位失败而中断。此时必须先用 DBMS_LOB.SUBSTR 配合 ROWID 分段读取,或改用底层工具如 ODU 直接解析数据文件。
为什么 ALTER TABLE ... MOVE 大概率失败
很多人第一反应是重建段:ALTER TABLE t MOVE TABLESPACE new_ts。但这个操作需要完整读取原段所有块来重写,只要中间遇到一个坏块,就会报 ORA-01578 并中止,无法“尽力而为”。更危险的是,MOVE 过程中若发生中断,可能留下半成品段,导致原表彻底不可访问。
除非你已通过 DBV 确认坏块集中在最后几个 extent,且表数据量不大,否则不要在生产库上直接执行 MOVE。优先走 ROWID 分段抽取 + 重建空表 + INSERT /*+ APPEND */ 导入的路径。
段级抢救最易被忽略的一点:坏块位置不是随机的。它往往聚集在某个文件的特定区域(比如磁盘坏道对应的一段 LBA),所以第一次成功读出的块范围,大概率能复用到其他同文件的表——别急着删掉临时脚本,留着下次还能用。


















