Oracle UNDO 数据文件丢失处理(案例一)
本文档详细阐述了在 Oracle 数据库归档模式下,因 shutdown abort 导致 UNDO 数据文件丢失后的恢复步骤。此场景较为特殊,处理不当可能导致数据不一致,操作需谨慎。
一、案例环境
- 操作系统: Linux x86-64
- 数据库版本: Oracle 11gR2
- 归档模式: Archivelog
二、案例过程概述
- 创建测试表并插入数据,模拟一个未提交的事务。
- alter system flush buffer_cache;刷新缓存,确保数据写入磁盘
- 使用
shutdown abort命令强制关闭数据库。 - 模拟 UNDO 数据文件丢失(例如,使用
mv命令移动文件)。 - 数据库mount状态下offline drop掉undo数据文件
- alter database open打开数据库
- 完成后续处理
三、核心概念:两个关键的隐藏参数
在处理 UNDO 段损坏或丢失的问题时,Oracle 提供了两个隐藏参数:_offline_rollback_segments 和 _corrupted_rollback_segments。理解它们的区别至关重要。
当数据库启动(OPEN)时,任何在这两个参数中列出的回滚段(Rollback Segments)都会:
- 不会被扫描,其上的活动事务既不会被标记为死亡,也不会被回滚。
- 在数据字典表
undo$中显示为OFFLINE状态。 - 不能被实例用于新的事务。
_offline_rollback_segments 参数
- 核心行为 : 在数据库
OPEN阶段,Oracle 仍然会尝试读取 此参数列出的 UNDO 段。 - 事务处理:
- 如果查询数据块时发现活跃事务,并指向这些 UNDO 段,Oracle 会尝试读取其事务表。
- 若事务已提交,则进行数据块清除(Block Cleanout)。
- 若事务仍活动,则生成一致性读(CR)块。
- 关键点 : 如果读取这些 UNDO 段时发生错误(如文件丢失或块损坏),错误会被记录到
alert.log中,但不会阻止数据库启动。然而,查询该数据的会话会因错误而终止。
_corrupted_rollback_segments 参数
- 核心行为 : 在数据库
OPEN阶段,Oracle 完全不会访问 此参数列出的 UNDO 段。 - 事务处理:
- 所有指向这些 UNDO 段的事务都被假定已经提交。
- 这会进行延迟块清除(Delayed Block Cleanout),效果类似于该回滚段已被删除。
- 风险警告 : 此方法极易导致严重的逻辑数据不一致 。如果数据字典或自举(bootstrap)核心对象上有活跃事务,数据库可能无法打开(报
ORA-00704错误)。强烈建议在使用此参数打开数据库后,立即导出数据并重建数据库。
四、案例详细步骤
以下示例演示了使用 _offline_rollback_segments 参数进行恢复的完整流程。
1. 模拟 UNDO 数据文件丢失
sql
-- 1. 创建测试表并插入数据
SQL> create table test as select * from dba_objects;
Table created.
SQL> select count(*) from test;
COUNT(*)
----------
71896
SQL> insert into test select * from test; -- 未提交事务
71896 rows created.
SQL> alter system flush buffer_cache; -- 刷新缓存,确保数据写入磁盘
System altered.
SQL> quit
-- 2. 强制关闭数据库
SQL> shutdown abort;
ORACLE instance shut down.
-- 3. 在操作系统层面模拟文件丢失
[oracle@netdbhost dbs]$ mv /u01/test/test/undotbs1.dbf /u01/test/test/undotbs1.dbfbak
2. 启动数据库报错
尝试启动数据库时,会因找不到 UNDO 数据文件而报错。
sql
SQL> startup
ORACLE instance started.
...
Database mounted.
ORA-01157: cannot identify/lock data file 3 - see DBWR trace file
ORA-01110: data file 3: '/u01/test/test/undotbs01.dbf'
3. 将丢失的数据文件 OFFLINE DROP
在 MOUNT 状态下,将丢失的数据文件离线并删除。此操作允许数据库继续打开。
sql
-- 查询确认丢失的文件名
SQL> select name from v$datafile where file#=3;
NAME
----------------------------------------------------
/u01/test/test/undotbs01.dbf
-- 执行 offline drop
SQL> alter database datafile '/u01/test/test/undotbs01.dbf' offline drop;
Database altered.
4. 打开数据库并检查 UNDO 段状态
此时数据库可以成功打开,但需要检查 UNDO 段的状态。
sql
SQL> alter database open;
Database altered.
-- 查看回滚段状态,会发现大量 NEEDS RECOVERY 状态的段
SQL> select status, count(*) from dba_rollback_segs group by status;
STATUS COUNT(*)
------------------ ----------
NEEDS RECOVERY 10
ONLINE 1
-- 查看具体的回滚段名称
SQL> select segment_name, status from dba_rollback_segs where status='NEEDS RECOVERY';
SEGMENT_NAME STATUS
-------------------------------- -----------------
_SYSSMU1_3780397527$ NEEDS RECOVERY
_SYSSMU2_2232571081$ NEEDS RECOVERY
... (共10个)
5. 创建新的 UNDO 表空间
创建一个新的 UNDO 表空间,并切换过去。
sql
-- 创建新的 UNDO 表空间
SQL> create undo tablespace undo2 datafile '/u01/test/test/undo2.dbf' size 50m;
Tablespace created.
-- 切换当前实例使用的 UNDO 表空间
SQL> alter system set undo_tablespace='undo2';
System altered.
-- 为后续操作,将 UNDO 管理方式改为手动(需重启生效)
SQL> alter system set undo_management=manual scope=spfile;
System altered.
6. 使用 PFILE 和隐藏参数重启数据库
这是最关键的一步。通过创建 PFILE 并添加 _offline_rollback_segments 参数,告知 Oracle 忽略那些丢失的回滚段。
sql
-- 从 SPFILE 创建 PFILE
SQL> create pfile='/home/oracle/dhtest.ora' from spfile;
File created.
-- 关闭数据库
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
编辑 /home/oracle/dhtest.ora 文件,确保包含以下两行(_offline_rollback_segments 的值为上一步查出的所有 NEEDS RECOVERY 的段名):
*.undo_management=manual
*._offline_rollback_segments=('_SYSSMU1_3780397527$','_SYSSMU2_2232571081$','_SYSSMU3_2097677531$','_SYSSMU4_1152005954$','_SYSSMU5_1527469038$','_SYSSMU6_2443381498$','_SYSSMU7_3286610060$','_SYSSMU8_2012382730$','_SYSSMU9_1424341975$','_SYSSMU10_3550978943$')
使用修改后的 PFILE 启动数据库:
sql
SQL> startup pfile=/home/oracle/dhtest.ora
ORACLE instance started.
...
Database mounted.
Database opened.
-- 验证数据,会发现未提交的数据被视为已提交
SQL> select count(*) from dh.test;
COUNT(*)
----------
143792
-- 再次检查回滚段状态,原 NEEDS RECOVERY 的段已变为 OFFLINE
SQL> select status, count(*) from dba_rollback_segs group by status;
STATUS COUNT(*)
------------------ ----------
NEEDS RECOVERY 10
OFFLINE 10
ONLINE 1
7. 手工删除丢失的 UNDO 段
现在可以安全地删除那些已离线的回滚段。
sql
SQL> drop rollback segment "_SYSSMU1_3780397527$";
Rollback segment dropped.
-- ... 依次删除所有10个回滚段 ...
SQL> drop rollback segment "_SYSSMU10_3550978943$";
Rollback segment dropped.
-- 确认删除结果
SQL> select status, count(*) from dba_rollback_segs group by status;
STATUS COUNT(*)
------------------ ----------
OFFLINE 10
ONLINE 1
8. 清理并恢复正常配置
最后,删除旧的 UNDO 表空间,并恢复自动 UNDO 管理模式。
sql
-- 删除旧的 UNDO 表空间及其数据文件
SQL> drop tablespace undotbs1 including contents and datafiles;
Tablespace dropped.
-- 关闭数据库
SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
-- 使用 SPFILE 正常启动
SQL> startup
ORACLE instance started.
...
Database mounted.
Database opened.
-- 将 UNDO 管理改回自动模式
SQL> alter system set undo_management=auto scope=spfile;
System altered.
-- 再次重启使配置生效
SQL> shutdown immediate;
SQL> startup
至此,数据库恢复完毕,UNDO 表空间丢失的问题已解决。