oracle实例在crash后,undo如何恢复的比较难于理解。下面详细描述了undo 的DML操作是如何保存在undo tablespace和在线归档日志中的。然后在分析下undo保存两份信息的原因。
首先,看看undo是如何保存在undo tablespace中的。 –oracle redo 包含 undo
– Checkpointing 导致
. 脏块被写到数据文件中
. 同样的包含undo信息的buffers 也被写到undo tablespace
概述: – 创建tablespace test_undo
– 创建表 test_undo_tab in tablespace test_undo
– Insert two rows with txt – teststring1, teststring2 in test_undo_tab 然后执行checkpoint。
– 切换日志,新在线日志中没有包含string teststring1
– 切换undo表空间到表空间undotbs2
– 将字段从 teststring1 更新为teststring_uncommitted,但是不commit
– 在另外一个会话,将字段值从 teststring2 更新为teststring_committed并commit。
– 检查是不是字段的修改前后的值都写到现在的redo log文件中。
– 检查undo表空间没有包含修改前的值,例如:
teststring1 和 teststring2 作为undo信息还在buffer cache中,checkpoint还没有发生。
– 执行手工的checkpoint,buffers中的信息包含undo信息都会刷新到磁盘中。
– 检查undo表空间是否包含修改前的值。例如teststring1 和 teststring2
执行:
– 正在使用的undo tablespace – sql>sho parameter undo_tablespace NAME TYPE VALUE ———————————————– —————————— undo_tablespace string UNDOTBS1
– 创建一个新的undo表空间undotbs2 SQL> create undo tablespace undotbs2datafile ‘/paic/app/dbshare/undotbs2.dbf’ size 100m;
– 创建一个表空间test_undo SQL> drop tablespace test_undo includingcontents and datafiles; create tablespace test_undo datafile ‘/paic/app/dbshare/test_undo.dbf’ size 128k;
– 在上面创建的表空间中创建测试表test_undo_tab SQL> drop table test_undo_tab purge; create table test_undo_tab(txt char(1000)) tablespace test_undo;
– 在test_undo_tab表中insert两条记录并commit – SQL> insert into test_undo_tab values(‘teststring1’); insert into test_undo_tab values (‘teststring2’); commit;
– 执行手工checkpoint,然后上述修改写到数据文件中。 SQL>alter system checkpoint;
– 将当前的undo表空间切换为undotbs2 SQL>alter system set undo_tablespace =undotbs2; show parameter undo_tablespace NAME TYPE VALUE ———————————————– —————————— undo_tablespace string UNDOTBS2
– 切换redo log,这样当前的redolog没有包含任何信息。 SQL>alter system switch logfile;
– 找出现有的redo log SQL> col member for a30 select member, l.status from v$log l, v$logfile f where l.group# =f.group# and l.status = ‘CURRENT’;
MEMBER STATUS ———————————————- +DATA_TEST1_DG/test/onlinelog CURRENT /group_2.384.865438859
– 打开一个会话更新一条记录但是不commit– SQL> update test_undo_tab set txt = ‘teststring_uncommitted’ where txt = ‘teststring1’;
– 打开另外一个会话,修改第二条记录,并commit – SQL> update test_undo_tab set txt = ‘teststring_committed’ where txt = ‘teststring2’; commit;
– 然后检测redo log文件中是否包含redo和undo的DML操作 (committed and uncommitted)– [grid@cnsh230235 ~]$ asmcmd cp+DATA_TEST1_DG/weiyj/onlinelog/group_3.401.865438859 /oracle_grid/grid/group_2 copying+DATA_TEST1_DG/weiyj/onlinelog/group_2.384.865438859 ->/oracle_grid/grid/group_2 [grid@cnsh230235 ~]$ strings group_2 |greptest teststring_uncommitted teststring1 teststring_committed teststring2
– 检测数据文件没有包含修改的值,比如teststring_committed 和 teststring_uncommitted ,因为checkpoint还没有发生,脏块还没有写入到磁盘。 [oracle@node1 oracle]$ strings/u01/app/oracle/test_undo.dbf|grep teststring
teststring2 , teststring1
– 检测undo表空间也没有包含更新前的值,比如undo的信息teststring1 和teststring2还在buffer cache中,checkpointing还没有发生。 [oracle@node1 oracle]$ strings/u01/app/oracle/undotbs2.dbf|grep teststring
– 执行手工的checkpoint,这样buffers中的undo信息就被刷新到磁盘中。 SQL> alter system checkpoint;
– 检测数据文件包含了修改前后的值,(both committed anduncommitted) ,例如: teststring_committed andteststring_uncommitted,因为checkpointing发生后, buffers中的脏块被刷新到磁盘中。 [oracle@node1 oracle]$ strings/u01/app/oracle/test_undo.dbf|grep teststring
teststring_committed , teststring_uncommitted
– 检测undo表空间中也包含了修改前的值,比如teststring1 and teststring2 [oracle@node1 oracle]$ strings/u01/app/oracle/undotbs2.dbf| grep teststring
teststring2 teststring1
– 清理测试文件 – SQL>drop tablespace test_undo includingcontents and datafiles; alter system set undo_tablespace=undotbs1; drop tablespace undotbs2 including contents and datafiles;
现在可以得出结论:
–Log writer 会写一个事务中每个DML的undo和redo (committed/uncommitted) 和是否发生checkpoint没有关系。
– 发生checkpoint时,
. database buffer cache中的undo信息会写到undo表空间中
. 脏块Dirty buffers中包含了committed/uncommitted数据会被写入到数据文件中。
如果是一个正在运行中数据库,这意味着: – 数据文件可能会包含未提交的信息 (被修改的buffers、但是没有commited、发生checkpoint) – 提交的数据没有在数据文件中(提交后,checkpointing还没有发生) – 修改后提交的redo/undo会被写入到redo logs中 (Lgwr writes on commit). – 修改后没有提交的redo/undo可能被写入到redo logs中(Lgwr writes every 3 seconds) – 除了redo logs文件,undo information § . 可能会在buffer cache中 (修改后checkpoint还没有发生) § . checkpoint发生后会被写入到undo表空间中。 § . 当一个事物活动、没有结束时,undo信息是不会被覆盖的。
另外一个问题时,为什么undo信息需要保存两份,分别保存在undo表空间和redo logs文件中。
让我们看看如果undo信息只保存在redo log文件中,会发生什么情况。
只要修改的数据已经写到数据文件中,redo log可以被重用。
如果进行了修改,但是没有commit的情况下:
– 修改被写到了redo log文件中
– 发生了checkpoint
– uncommitted的修改被写到了数据文件中
– 如果决定回退修改
– 如果redo log文件没有被覆盖重用
. 搜索整个redo log文件,然后进行回退
如果覆盖重用了
. undo的信息就不能进行恢复了。
有人可能会说,如果redo log包含了活动的undo信息,就不允许覆盖重用, 我们就可能只需要保存undo到redo log文件中了。这种方案也是不可行的,因为:
– redo log文件的大小将会增至的非常大,因为极端情况下,一个用户可能过几个月才结束一个事务。
– 为了回退一个修改,需要读取大量redo log文件中的数据,包括redo和undo信息,这将带来性能极大的下降。
– redo log文件将会出现争用,因为它们被用作:
. 同时写 redo and undo
. 用来读取回退修改
所以,undo信息除redo log之外还需要独立保存用来回退没有commit的事务。Undo信息会保存在undo buffers/undo tablespace中,用来:
– read consistency
– flashback query
– flashback version query
现在, 让我们看看如果undo只保存在undo表空间中,会出现什么情况。
情况– I – 进行修改但是没有提交
– 修改的redo信息保存在redo log文件中
– 修改的undo信息保存在buffer cache
– 修改的undo信息还没有保存到undo表空间中,因为还没有发生checkpoint
– 实例 crashes
– 保存在buffer cache的undo信息被清除了。
– 作为instance recovery的一部分, redo被应用后,数据文件保护了未commit的数据。
– 数据库不能被打开,因为没有undo信息来需要回退未提交的修改,这样数据库处于不一致的状态。
情况– II – 进行表空间的hot backup
– 表空间的数据库文件丢失或者损坏了
– 将表空间offline,然后从备份restore数据文件
– recover the datafile
– redo logs和archivelogs包含了提交和未提交事务的redo数据
– redo logs和archivelogs没有包含undo的信息(根据假设).
– 作为recovery的一部分, 提交和未提交事务的redo数据可以读取并应用。
– 表空间还是不能修改为在线,因为需要进行回滚的undo信息没有找到。
所以,为了解决上面的问题,undo也需要保存在redo log文件中。在实例或者media recovery时,进行向前回滚,redo的信息(redo log文件中)被应用到数据文件,undo的信息被用来生成undo segments。在实例或者media recovery时,进行向后回滚,这些undo segments后续被用来做回退没有提交的修改。