一、故障突发:一场始料未及的 "存储罢工"
2024 年 X 月 X 日上午 9 点,某客户运维团队紧急联系我们:核心业务系统突然无法访问,数据库启动失败,业务完全中断。客户方初步排查后反馈:外置存储可能出现硬件故障,直接影响了数据库的关键数据文件。
我们第一时间远程接入客户环境,确认基础信息:
二、排查之路:从 "启动失败" 到 "数据可见" 的突围
面对紧急情况,我们先做了两件事:对当前数据库状态做冷备保留现场,将故障数据文件设为 offline 避免进一步影响。
-- 冷备关键文件(示例路径)
cp /oradata/system01.dbf /backup/system01.dbf_bak cp /oradata/undotbs01.dbf /backup/undotbs01.dbf_bak
-- 将故障数据文件设为offline
alter database datafile '/oradata/corrupt_df.dbf' offline;
随后开始针对性排查:
1. 解开 Undo 的 "死结"
数据库启动时报错与 undo 段相关,初步诊断发现部分 undo 段因数据文件损坏已不可用。我们立即屏蔽了损坏的 undo 段,并切换到新的 undo 表空间 —— 这一步让数据库启动的第一个障碍被清除。
-- 屏蔽旧undo段、创建新的undo表空间
alter system set undo_management=manual scope=spfile; alter system event='10513 trace name context forever,level 2' scope=spfile; shutdown immediate startup create undo tablespace undotbs2 datafile '/oracle/app/oradata/orcl/undotbs02.dbf' size 3G autoextend off; alter system set undo_tablespace='undotbs2' scope=both; undo_management=auto shutdown immediate startup 利用隐含参数_offline_rollback_segments和_corrupted_rollback_segments屏蔽掉有问题的undo segments,然后开打数据库,最后重建undo或者drop掉损毁的回滚段即可 通过查询dba_rollback_segs定位需要屏蔽的undo段 undo_management=‘manual’ *._offline_rollback_segments=(‘_SYSMU1_280145294$’,'_SYSMU2_280145294$’) —离线的段 *._corrupted_rollback_segments=(‘_SYSMU1$’,'_SYSMU2$’) —允许有问题的段
2. 修复 "破碎" 的数据块
通过 Oracle dbv坏块修复工具扫描,定位到多个损坏的数据块。我们对可修复的块进行了修复,对无法修复的块,删除了关联的索引(避免索引扫描触发错误)。
-- 使用DBMS_REPAIR包创建修复表DBMS_REPAIR.ADMIN_TABLES
declare v_flag boolean; begin v_flag := dbms_repair.admin_tables( table_name => 'REPAIR_TABLE', table_type => dbms_repair.repair_table, action => dbms_repair.create_action, tablespace => 'SYSTEM' ); end; /
-- 检查损坏块
SQL> set serveroutput on SQL> DECLARE num_corrupt INT; BEGIN num_corrupt := 0; DBMS_REPAIR.CHECK_OBJECT ( SCHEMA_NAME => 'MARY', OBJECT_NAME => 'T3', REPAIR_TABLE_NAME => 'REPAIR_TABLE', corrupt_count => num_corrupt); DBMS_OUTPUT.PUT_LINE('number corrupt: ' || TO_CHAR (num_corrupt)); END; /
-- 修复可修复的坏块
begin dbms_repair.fix_corrupt_blocks( schema_name => 'XXX', object_name => 'SECRET_TABLE_OLD', repair_table_name => 'REPAIR_TABLE', fix_count => :fix_count ); end; /
如果坏块发生在FREELIST列表中的中部,则FREELIST列表后面的块都无法访问
BEGIN DBMS_REPAIR.REBUILD_FREELISTS ( SCHEMA_NAME => 'YANGTK', OBJECT_NAME => 'TEST', OBJECT_TYPE => dbms_repair.table_object); END; /
设置DML跳过坏块DBMS_REPAIR.SKIP_CORRUPT_BLOCKS
SQL> select skip_corrupt from dba_tables where table_name='T3'; -------- DISABLED BEGIN DBMS_REPAIR.SKIP_CORRUPT_BLOCKS ( SCHEMA_NAME => 'MARY', OBJECT_NAME => 'T3', OBJECT_TYPE => dbms_repair.table_object, FLAGS => dbms_repair.SKIP_FLAG); END; / SQL> select skip_corrupt from dba_tables where table_name='T3'; -------- ENABLED
坏块skip后,此表相关的索引全部drop、而后create、不要用rebuild方式重建
-- 删除损坏索引(示例索引名)
drop index XXX.IDX_SECRET_TABLE_OLD_01;
3. 数据库终于 "醒了"
经过上述操作,数据库成功启动到 open 状态!但我们知道,这只是第一步 —— 启动成功不代表数据完好。
-- 启动数据库至open状态
startup mount; alter database open;
4. 导出遇阻:核心表 "藏着" 问题
为了完整备份数据,我们启动全库逻辑导出,却在导出XXX.SECRET_TABLE_OLD表时卡住了:expdp 报错,换成 exp 同样失败。这个表是客户的核心库存表,约 13 万条记录,必须拿下。
-- expdp导出命令(报错)
expdp system/xxx directory=exp_dir full=y logfile=full_exp.log
-- 尝试单独导出核心表(报错)
expdp system/xxx directory=exp_dir tables=XXX.SECRET_TABLE_OLD logfile=secret_table_exp.log
-- 改用exp导出(仍报错)
exp system/xxx tables=XXX.SECRET_TABLE_OLD file=secret_table.dmp log=secret_table_exp_old.log
三、聚焦核心表:被损坏的数据 "藏在哪里"?
我们把目标锁定在XXX.SECRET_TABLE_OLD表,通过一系列测试找到了问题的关键:
- 查询不完整:执行select * from XXX.SECRET_TABLE_OLD;时,会在中途报数据块损坏错误,无法读取全表数据。
- 部分记录 "读不出":尝试用create table new_table as select * from ...复制表,直接报错; 但限制rownum%20<%2079000时能成功创建,超过则失败—— 说明 79000 条记录后存在损坏区域。
-- 直接复制表(报错)
create%20table%20XXX.SECRET_TABLE_COPY%20as%20select%20*%20from%20XXX.SECRET_TABLE_OLD;
-- 限制rownum复制(成功)
create%20table%20XXX.SECRET_TABLE_COPY_PART%20as%20select%20*%20from%20XXX.SECRET_TABLE_OLD%20where%20rownum%20<%2079000;
- rowid 暴露的 "真相":我们打印了表中所有记录的 rowid,逐段验证发现:79000 条后并非全是坏数据,而是"好坏掺杂"—— 部分记录的 rowid 对应的块完好,能正常查询;部分则因块损坏无法读取。
-- 打印所有rowid
create%20table%20XXX.rowid_list%20as%20select%20rowid%20rid%20from%20XXX.SECRET_TABLE_OLD;
-- 验证特定rowid记录(示例)
select%20*%20from%20XXX.SECRET_TABLE_OLD%20where%20rowid='AAAR8cAAEAAAAAeAAA';%20-- 完好记录 select%20*%20from%20XXX.SECRET_TABLE_OLD%20where%20rowid='AAAR8cAAEAAAAAeAAB';%20-- 损坏记录(报错)
四、数据抢救:用 rowid"筛出" 完好记录
针对 "好坏掺杂" 的情况,我们设计了一套 "逐行筛查" 的方案,最大限度提取可用数据:
1. 建临时表存 rowid
先创建临时表XXX.table2024,存储原表所有记录的 rownum 和 rowid(相当于给每条记录编一个 "身份证号"):
create%20table%20XXX.table2024(TEXTID%20varchar2(500),%20ROWTEXT%20varchar2(500)); insert%20into%20XXX.table2024%20select%20rownum,%20rowid%20from%20XXX.SECRET_TABLE_OLD; commit;
2. 建目标表 "接数据"
创建与原表结构一致的空表XXX.SECRET_TABLE_NEW2024,作为恢复后的数据落地表:
CREATE%20TABLE%20XXX.SECRET_TABLE_NEW2024%20AS%20SELECT%20*%20FROM%20XXX.SECRET_TABLE_OLD%20WHERE%201=0;
3. 写存储过程 "逐个过筛"
编写存储过程TAB_insert,循环读取临时表的 rowid,逐条尝试插入新表 —— 遇到损坏记录时,利用异常处理跳过,确保流程不中断:
CREATE%20OR%20REPLACE%20PROCEDURE%20TAB_insert AS cursor%20tab_cur%20is SELECT%20TEXTID,ROWTEXT%20FROM%20XXX.table2024; begin for%20tab_recoder%20in%20tab_cur%20loop begin insert%20into%20XXX.SECRET_TABLE_NEW2024 select%20PART_ID,UNIT_ID,STOCK_ID,PART_NAME,STOCK_NUM,BAD_NUM,GIVE_NUM,IN_PRICE,ACTUAL_PRICE,TOTAL_PRICE,TAX_RATE,TAXES_PRICE,STOCK_PLACE,MIN_STOCK_NUM,MAX_STOCK_NUM,MEMO%20from%20XXX.SECRET_TABLE_OLD where%20rowid%20=%20tab_recoder.ROWTEXT; commit; EXCEPTION%20WHEN%20others%20THEN%20null;%20-- 遇到错误直接跳过 end; end%20loop; end; / execute%20tab_insert();
4. 结果:138823 条记录 "获救"
执行完成后,新表SECRET_TABLE_NEW2024共有 138823 条记录,对比原表的 138928 条,仅缺失 105 条 —— 核心数据几乎完整恢复!
-- 统计原表与新表数据量
select%20count(*)%20from%20XXX.SECRET_TABLE_OLD;%20--%20138928条记录 select%20count(*)%20from%20XXX.SECRET_TABLE_NEW2024;%20--%20138823条记录
五、总结与参考
关键措施
- 硬件层面:存储厂商修复了硬件故障,消除了根本诱因;
- 数据库启动:通过屏蔽坏 undo 段、修复坏块、调整参数,让数据库恢复运行;
- 数据恢复:利用 rowid 定位 + 存储过程逐行筛查,从损坏表中 "抢救" 出 99.9% 的有效数据。
参考文档
本次恢复思路参考了 Oracle%20MOS 文档:《Extract%20rows%20from%20a%20CORRUPT%20table%20creating%20ROWID%20from%20DBA_EXTENTS》(Doc%20ID%20422547.1)。
编辑推荐:
- 存储裂缝之“救赎” ——Oracle 数据坏块故障修复纪实03-03
- 定义编辑框:与输入法交互的3个核心步骤?03-03
- Oracle 23ai Datatype Limits有哪些?03-03
- In-memory不要全加载怎么做?03-03
- 子夜代码:当调度器陷入沉睡03-03
- 分发平台有哪些?怎么选择合适的内测分发平台03-03
- MySQL与其他数据库产品的比较,优势在哪里?03-03
- [20250807]绑定变量peeking的问题.txt03-03
相关推荐
-
雷神推出 MIX PRO II 迷你主机:基于 Ultra 200H,玻璃上盖 + ARGB 灯效
2 月 9 日消息,雷神 (THUNDEROBOT) 现已宣布推出基于英
-
制造商 Musnap 推出彩色墨水屏电纸书 Ocean C:支持手写笔、第三方安卓应用
2 月 10 日消息,制造商 Musnap 现已在海外推出一款 Oce
热文推荐
- 存储裂缝之“救赎” ——Oracle 数据坏块故障修复纪实
存储裂缝之“救赎” ——Oracle 数据坏块故障修复纪实
26-03-03 - Oracle 23ai Datatype Limits有哪些?
Oracle 23ai Datatype Limits有哪些?
26-03-03 - In-memory不要全加载怎么做?
In-memory不要全加载怎么做?
26-03-03 - 子夜代码:当调度器陷入沉睡
子夜代码:当调度器陷入沉睡
26-03-03 - 使用什么命令能删除 FOREIGN ARCHIVED LOG
使用什么命令能删除 FOREIGN ARCHIVED LOG
26-03-03 - 记一次Oracle数据库归档日志暴增故障案例分析
记一次Oracle数据库归档日志暴增故障案例分析
26-03-03 - 外连接嵌套循环为何无法更改驱动表
外连接嵌套循环为何无法更改驱动表
26-03-03 - Oracle AWR夺命33问,你能过几关?
Oracle AWR夺命33问,你能过几关?
26-03-03 - 东北已经装上空调,欧洲人还在“装”?
东北已经装上空调,欧洲人还在“装”?
26-03-03 - OGG 同步奇案:医疗数据 “消失” 之谜
OGG 同步奇案:医疗数据 “消失” 之谜
26-03-03

