如果说Redo是用来保证在故障时事务可以被恢复,那么Undo则是用来保证事务可以被回退或者撤销。
在修改操作中,对于回退段的操作存在多处,在事务开始时,首先需要在回滚段表空间获得一个事务槽,分配空间,然后创建前镜像,此后事务的修改才能进行,Oracle必须以此来保证事务是可以回退的。
如果用户提交了事务,Oracle会在日志文件记录提交,并且写出日志,同时会在回滚段中把该事务标记为已提交;如果用户回滚事务,则Oracle需要从回滚段把前镜像数据读取出来,修改数据缓冲区,完成回滚,这个过程本身也要产生Redo,所以回退这个操作是很昂贵的。
在Oracle性能优化中,有一个性能指标称为平均事务回滚率(Rollback per transaction),用来衡量数据库的提交与回滚效率。可以在Statspack中找到这个指标。
该参数计算公式为:
Round(User rollbacks/(user commits+user rollbacks),4)*100%
其中user commits和user rollbacks数据来自系统的统计信息,可以从v$sysstat视图中得到,
SQL> select name,value from v$sysstat
2 where name in ('user commits','user rollbacks');
NAME VALUE
---------------------------------------------------------------- ----------
user commits 5
user rollbacks 0
这个指标应该接近于0,如果该指标过高,则说明数据库的回滚过多。回滚过多不仅说明数据库经历了太多的无效操作,而且这些操作会极大影响数据库性能。
回滚段存储的内容:
-
对于INSERT操作,回滚段只需要记录插入记录的rowid,如果回退,只需将该记录根据rowid删除即可;
-
对于UPDATE操作,回滚段只需要记录被更新字段的旧值即可(前镜像),回退时通过旧值覆盖新值即可完成回退;
-
对于DELETE操作,Oracle则必须记录整行的数据,在回退时,Oracle通过一个反向操作恢复删除的数据。
通过以上可以总结:对于相同数据量的数据操作,通常INSERT产生最少的Undo,Update产生的Undo居中,而Delete操作产生的Undo最多。这也就是我们经常看到,当一个大的DELETE操作失败或者回滚,总是需要很长的时间,并且会有大量的Redo生成。所以通常在进行大规模数据删除操作时,推荐通过分批删除分次提交,以减少对于回滚段的占用和冲击。
并发控制和一致性读的实现
一方面Oracle通过锁定机制实现数据库的并发控制;一方面通过多版本模型来进行并发数据访问。通过多版本架构,Oracle实现了读取和写入的分离,使得写入不阻塞读取,读取不阻塞修改。
多版本模型在Oracle数据库中是通过一致性读来实现的,一致性读也正是回滚段表空间的主要作用之一。
Oracle一方面不允许其他用户读取未提交数据,另一方面保证用户读取的数据要来自同一时间点。
Oracle内部使用SCN作为数据库时钟,查询结果集就是根据SCN来进行判断的,每个数据块头部都会记录一个提交SCN,当数据更改提交后,提交SCN同时被修改,这个SCN在查询时用来进行一致性读判断。
比如:假定查询开始的时间为T1,则在查询获取的数据块中,如果数据块的提交SCN小于T1,则Oracle接受该数据,如果提交SCN大于T1或者数据被锁定修改尚未记录COMMIT SCN,则Oracle需要通过回滚段构造前镜像来返回结果,这就是一致性读的本质含义。
回滚段的前世今生
在Oracle 9i之前,回滚段表空间创建之后,Oracle随后创建回滚段供数据库使用,也可以手工创建或者删除回滚段进行维护,在开始事务之前,也可通过如下命令指定用户想要使用的回滚段。
set transaction user rollback segment <rollback_segment_name>;
可以从数据库中查询这些回滚段的状态:
SQL> select segment_name,tablespace_name,status from dba_rollback_segs;
SEGMENT_NAME TABLESPACE_NAME STATUS
------------------------------ ------------------------------ ---------------
SYSTEM SYSTEM ONLINE
_SYSSMU1$ UNDOTBS1 ONLINE
_SYSSMU2$ UNDOTBS1 ONLINE
_SYSSMU3$ UNDOTBS1 ONLINE
_SYSSMU4$ UNDOTBS1 ONLINE
_SYSSMU5$ UNDOTBS1 ONLINE
_SYSSMU6$ UNDOTBS1 ONLINE
_SYSSMU7$ UNDOTBS1 ONLINE
_SYSSMU8$ UNDOTBS1 ONLINE
_SYSSMU9$ UNDOTBS1 ONLINE
_SYSSMU10$ UNDOTBS1 ONLINE
从Oracle 9i开始,Oracle引入了自动管理的Undo表空间,如果选择使用自动的Undo表空间的管理,那么用户不再能够创建或删除回滚段,也不再需要为事务指定回滚段,这一切由Oracle自动进行。
伴随自动的Undo管理功能的引入,Oracle随之引入了几个新的初始化参数:
Undo_management:用来定义数据库使用的回滚段是否使用自动管理模式,该参数有两个可选项,AUTO表示自动管理,MANUAL表示手工管理。
Undo_tablespace:用来定义在自动管理模式下,当前实例使用哪个undo表空间。
Undo_suppress_errors:表示当使用自动管理模式时,如果使用不再支持的操作时(如为事务指定回滚段)是否返回出错信息。设置为True时不返回出错信息,操作无效但是可以继续,设置为False时,则操作不能继续,这实际上是一个向后兼容的参数。该参数在Oracle10g中已经被舍弃。
Undo_retention:表示在自动管理模式下,当回滚段变得非激活之后,回滚段中的数据在被覆盖前保留的时间,该参数单位是秒。在Oracle 9iR2中,这个参数的缺省值为10800秒,也就是3个小时。
在自动管理的Undo表空间下,回滚段的个数是Oracle根据数据库的繁忙程度自动分配或者回收的。缺省情况下数据库创建时初始化10个回滚段。
SQL> select * from v$rollname;
USN NAME
---------- ------------------------------
0 SYSTEM
1 _SYSSMU1$
2 _SYSSMU2$
3 _SYSSMU3$
4 _SYSSMU4$
5 _SYSSMU5$
6 _SYSSMU6$
7 _SYSSMU7$
8 _SYSSMU8$
9 _SYSSMU9$
10 _SYSSMU10$
11 rows selected.
在系统繁忙时,可以从数据库的alert_<sid>.log文件中看到回滚段的动态创建和释放过程。动态创建和释放,这也是自动管理的Undo表空间的优势之一。
整个事务过程解析的基本流程:
-
首先当一个事务开始时,需要在回滚段事务表上分配一个事务槽;
-
在数据块头部获得一个ITL事务槽,该事务槽指向回滚段段头的事务槽;
-
在修改数据之前,需要记录前镜像信息,这个信息以UNDO RECORD的形势存储在回滚段中,回滚段头事务槽指向该记录;
-
锁定修改行,修改行锁定位(lb-lock block)指向ITL事务槽;
-
数据修改可以进行。
块清除(Block Cleanouts)
在提交时,Oracle需要对数据块做哪些操作:
在事务需要修改数据时,必须分配ITL事务槽,必须锁定行,必须分配回滚段事务槽和回滚空间记录前镜像。当事务提交时,Oracle需要将回滚段上的事务表信息标记为非活动,以便空间可以重用;还有ITL事务信息和锁定信息需要清除,以记录提交。
由于Oracle在数据块上存储了ITL和锁定等事务信息,所以Oracle必须在事务提交之后清除这些事务数据,这就是块清除。块清除主要清除的数据有行级锁、ITL信息(包括提交标志、SCN等)。
如果提交时修改过的数据块仍然在Buffer Cache之中,那么Oracle可以清除ITL信息,这叫做快速块清除。快速块清除有一个限制,当修改的块数量超过Buffer Cache的约10%,则对超出部分不再进行快速块清除。
如果提交事务时,修改过的数据块已经被写回到数据文件上(或大量修改超出10%的部分),再次读出该数据块进行修改显然成本过于昂贵。对于这种情况,Oracle选择延迟块清除,等到下次访问该Block时再来清除ITL锁定信息。Oracle通过延迟块清除来提高数据库的性能,加快提交操作。
提交之后的Undo信息:
虽然事务已经提交,不可以回滚了,但是在覆盖之前,这个前镜像信息仍然存在,通过某种手段,仍然可以获得这个信息。
Oracle 9i闪回查询的新特性
SQL> select dbms_flashback.get_system_change_number scn from dual; --查询当前数据库的SCN
通过特定的语法,可以将历史SCN状态数据查询出来:
Select * from emp as of scn 89803404323255 where empno in(7788,7782);
由于这个查询需要从Undo中获取前镜像信息,如果Undo中的信息被覆盖,则以上查询将会失败。
观察回滚段的使用:
SQL> select usn,xacts,rssize,hwmsize from v$rollstat;
USN XACTS RSSIZE HWMSIZE
---------- ---------- ---------- ----------
0 0 385024 385024
1 0 2220032 2220032
2 0 516096 516096
3 0 450560 450560
4 0 2220032 2220032
5 0 450560 450560
6 0 5365760 5365760
7 0 2285568 2285568
8 0 2220032 2220032
9 0 450560 450560
10 0 9560064 9560064
Oracle10g闪回查询特性的增强
Oracle 9i的闪回查询只能提供某个时间点的数据视图,并不能告诉用户这样的数据经过了几个事务、怎样的修改(UPDATE、INSERT、DELETE等),而这些信息在回滚段中是存在的,在Oracle10g中,Oracle进一步加强了闪回查询的特性,提供了以下两种闪回查询:
闪回版本查询(Flashback Versions Query)
闪回事务查询(Flashback Transaction Query)
闪回版本查询允许使用一个新的VERSIONS子句查询两个时间点或者SCN之间的数据版本。这些版本可以按照事务区分,闪回版本查询只返回提交数据,未提交数据不被显示。
Oracle10g的闪回版本查询通过使用VERSIONS子句和对数据表引入一系列的伪列(version_starttime等),可以获得对数据表的所有事务操作,versions_operation代表不同类型的操作(D-DELETE、I_INSERT、U_UPDATE),VERSIONS_XID是一个重要依据,代表了不同版本的事务ID。
Select versions_starttime,versions_endtime,versions_xid,versions_operation,username,user_id
From tbl_name versions between timestamp minvalue and maxvalue;
通过以上查询,根据versions_xid可以清晰地区分不同事务在不同时间对数据所作的更改。
ORA—01555错误
由于回滚段是循环使用的,当事务提交以后,该事务占用的回滚段事务表会被标记为非活动,回滚段空间可以被覆盖重用。如果一个查询需要使用被覆盖的回滚段构造前镜像实现一致性读,那么此时会出现ORA-01555错误。
ORA-01555错误的另一个原因是因为延迟块清除。当一个查询触发延迟块清除时,Oracle需要去查询回滚段获得该事务的提交SCN,如果事务的前镜像信息已经被覆盖,并且查询SCN也小于回滚段中记录的最小SCN,那么Oracle将无法判断查询SCN和事务提交SCN的大小,此时出现延迟块清除导致的ORA-01555错误。
另外一种导致ORA-01555错误的情况出现在使用sqlldr直接方式加载(direct=true)数据时。当通过sqlldr direct=true方式加载数据时,由于不产生重做和回滚信息,Oracle直接指定Cached Commit SCN给加载数据,在访问这些数据时,有时会产生ORA-01555错误。
--下面这个例子挺好的个人觉得
假设在时间T用户A发出一条更新语句,更改SCOTT用户的SAL;用户B在TY时间发出查询语句,查询SCOTT用户的SAL;用户A的更新在TX时间提交,提交可能为快速提交清除,也可能是延迟块清除;用户B的查询在时间TZ输出。
(1)如果TY<T<TZ<TX,那么查询需要构造一致性读,由于事务尚未提交,可以通过回滚段构造前镜像,完成一致性读取。
(2)如果TY<T<TX<TZ,由于TY查询时间小于T事务更新时间,那么数据库需要构造一致性读取,而TZ查询完成时间大于TX提交时间,那么前镜像就有可能被覆盖,不能获取。
如果TX的提交方式为FAST BLOCK CLEANOUT,那么回滚段信息不可用时就会出现一致性读ORA-01555错误。
如果TX的提交方式为DELAYED BLOCK CLEANOUT,那么回滚段信息不可用时ORACLE将无法判断TY和YX的时间先后关系。如果TY>TX,那么ORACLE可以正常进行块清除,并将快清楚后的数据返回给用户B;如果TY<TX,那么ORACLE需要继续构造一致性读返回给用户B;ORACLE无法判断这两种情况(因为无法从回滚段中获取提交SCN 也就是无法知道TX 个人理解哦),就会出现延迟块清除ORA-01555错误。
ORA-01555的直观解释是“Snapshot too old”,也就是快照太旧,其根本含义就是查询需要的前镜像过于久远,已经无法找到了。当数据出现ORA-01555错误时,Oracle会将错误信息记录在警告日志中。
在Oracle 9i自动管理的Undo表空间模式下,undo_retention参数设置当事务提交之后,回滚段变得非激活,回滚段中的数据在被覆盖前保留的时间,该参数以秒为单位,9iR1初始值为900秒,在Oracle 9iR2增加为10800秒。
显然该参数设置的越高九越能够减少ORA-01555错误的出现,但是保留时间和存储空间是紧密相关的。如果Undo表空间的存储空间有限,那么Oracle就会选择回收已提交事务占用的空间,而置undo_retention参数的设置于不顾。
在Oracle 9i的AUM模式下,undo_retention实际上是一个非但保(No Guaranteed)限制。也就是说,如果有其他事务需要回滚空间,而空间出现不足时,这些信息仍然会被覆盖。
从Oracle10g开始,Oracle引入了自动的undo_retention调整,缺省情况下,这个功能被启用,Oracle动态的收集系统的事务信息,自动调整以满足最长运行查询的需要。当然如果空间不足,那么Oracle满足最大允许的长时间查询,而不再需要用户手工调整。
这个新特性的引入伴随着几个新的隐含初始化参数:
_undo_autotune enable auto tuning of undo_retention
_collect_undo_stats Collect statistics v$undostat
这两个参数缺省都是打开的。
同时Oracle10g增加了Guarantee控制,也就是说,用户可以指定Undo表空间必须满足Undo_retention的限制:
Alter tablespace undotbs1 retention guarantee;
Alter tablespace undotbs1 retention noguarantee;
在DBA_TABLESPACES视图增加了RETENTION字段用以描述该选项:
当Undo表空间设置为Guarantee,那么提交事务的回滚空间必须被保留足够的时间,如果Undo表空间的空间不足,那么新事务会因空间不足而失败,而不是选择之前的覆盖。