undo 表空间和回滚段

来源:互联网 发布:网络语言kfc是什么意思 编辑:程序博客网 时间:2024/04/27 18:45

undo表空间和回滚段

undo/rollback表空间是用于存放回滚段(rollback segment)的表空间。回滚段是Oracle用于保存被修改数据的前映象的数据空间。每个回滚段包含一些扩展,而回滚段采用一种循环机制来使用这些扩展。当某个扩展写满后,自动切换到另外一个扩展继续使用。一个事务会将回滚记录写在回滚段的当前位置,并且通过记录大小来标明记录的位置。当前写指针是回滚段段头中的一个控制结构。尾部指的是回滚段中最后一条记录的开始位置。
回滚段的数量和每个回滚段的大小对于回滚段的配置来说至关重要。在OLTP系统中,设置足够的回滚段数量,以避免回滚段冲突。每个回滚段的大小应该足够大,以便于适应事务处理的需要。Oracle循环使用回滚段,所有的在线回滚段(除了系统回滚段外)都会被轮流使用。Oracle在使用回滚段时,有以下特点。
一个事务只使用一个回滚段来记录所有的回滚记录。
多个事务可以写入相同的扩展。
扩展被循环使用,任何一个扩展都不会被跳过。
如果不能使用下一个扩展,Oracle会自动分配一个新扩展,并将其插入这个环中。
Oracle不会使用被尾部占据的扩展。
从上面的原则可以看出,事务的持续时间和事务的大小同样是影响回滚段的重要因素。一个哪怕只使用了一个字节的长时间的事务也可能导致回滚段扩展。如果一个系统中有多个回滚段,那么Oracle会根据下面的原则来选择一个回滚段。
首先,Oracle会选择当前事务数最少的回滚段。
如果有两个以上的回滚段内当前有相同的事务数,那么Oracle会根据上一个事务使用的回滚段号,选择比这个回滚段号大的下一个顺位的回滚段。比如当前事务使用了回滚段2,回滚段1和回滚段4有最少的事务数,那么Oracle会使用回滚段4来存储下一个事务的数据。
从上面的原则,我们能够在设置自己的回滚段时得到什么启发呢?如果你的回滚段足够大,那么首先需要根据实际的事务大小来确定回滚段的存储参数,使回滚段头不会很快转到段尾。如果回滚段很快转到尾部,就会导致回滚段很快进行扩展。
第二个应该注意的问题是,如果执行一个长时间运行的查询,这个查询访问的数据变化频率十分快,那么就需要保证回滚段的大小足够大,以保证你的查询结束之前不会出现ORA-1555错误(Snapshot too old )。
回滚段的大小取决于数据库中事务的特点。回滚段大小的设计应该满足数据库日常事务的要求,而不是针对那些不经常使用的大的事务。回滚段的数量设置要保证不会发生回滚段竞争。是否有回滚段竞争可以通过v$waitstat视图来查看,代码如下:

SELECT CLASS, COUNT FROM v$waitstat WHERE CLASS = '%undo%';
其中任何非0的COUNT值的存在都说明存在回滚段头的竞争。

|||||||||||||||||||||||||||||||||||||||||||||||

 

undo 也是由redo来保护的:

Oracle中undo的作用主要有两个:第一是回滚事务,第二是产生一致性读。同时也衍生出了一些新的 功能,比如Flashback query。传统的undo是通过undo segment来管理的,我们看下面的示例:

 

事务开始,必须首先在data block中分配ITL,ITL中记录了事务ID(XID),XID由三部分内容组成:XIDUSN(回滚段号),XIDSLOT(回滚段槽 号),XIDSQN(序列号),在Undo segment header中有一个事务表,记录该回滚段上的事务信息,每个事务都会占据了一个回滚槽,XID对应一个UBA(undo block address),表示该事务回滚信息的开始位置。 在上面的例子中,事务分别在T1,T2,T3时间执行了三个操作,更新了三个block中的数据,在每个data block中都存在一个ITL,指向undo segment header中的事务表。undo信息分别存放在三个undo block中,undo信息是一个链表结构,而undo segment header中的uba则指向了最后一个undo block,这也是回滚的起始位置。如果事务需要回滚,只需要在undo segment header中的事务表中找到事务回滚的起始位置,然后通过undo链表,就可以依次回滚整个事务。
细心的DBA一定会发现,在每个data block的ITL中也有一个UBA,实际上这个UBA是指向了该block对应的undo信息的起始位置,这个UBA主要的作用是提供一致性读,因为一 致性读需要通过undo信息来构造一个CR block,通过这个UBA就可以直接定位到block的回滚信息的起始位置,而不再需要通过undo segment header中的事务表。
在传统的undo管理模式中,Oracle对于undo和data block是一视同仁的,他们都需要首先读入到data buffer中进行修改,并都会产生redo信息,修改的过程大致是:产生undo的redo,更改undo block,产生data的redo,修改data block。总之redo必须要先于数据被记录下来。当数据库发生crash,可以通过redo日志,恢复data和undo block,然后再通过undo信息去回滚未提交的事务,保证数据的一致性,所以说instance recovery的过程是先前滚,再回滚的过程。
传统的undo管理有弊端,第一是undo信息如果不在data buffer中,必须首先从外部文件中读入;第二是undo的所有变化也必须同时记录redo,在事务提交时被写入到redo log中。Oracle提出了In-Memory UNDO的新特性,将undo信息都存放在内存结构中,缓解传统undo管理中带来的开销。
IMU在shared pool中分配一片内存空间(IMU pool),每个新的事务都会分配一个IMU buffer,它相当于一片事物私有的undo buffer,用来记录undo的信息。Data block中记录了IMU node的起始位置,通过IMU buffer中的信息就可以完成一致读,从而大大提升了效率。(这里要澄清一点,我在dump data block时,并没有发现指向IMU node的具体信息)。

 

在IMU模式下,undo信息依然会被写入到redo中,理解这点很重要!因为Instance recovery需要undo的信息去回滚未提交的事物,使数据库处于一致状态,如果redo中没有undo变化的信息,那么一旦发生Instance crash,数据库将有可能处于一个不一致的状态。
事务开始依旧会在data block中的分配ITL,并且它依然会指向undo segment header的事物表,但是undo block中的信息并不需要马上写入,这时undo信息是记录在IMU Buffer中的,这时也不会产生undo block的redo信息。在以下两种情况时,undo buffer中的信息会被写入到undo block中:1.IMU buffer空间不足;2.LGWR将redo信息被写入到redo log中时(比如commit),在v$sysstat中可以看到IMU flush和IMU commit,分别表示以上两种情况,如果你发现这两个值不断增加,代表系统开启了IMU特性。
现在我们已经了解到,IMU中的undo信息依旧会被写入到redo log中,只不过在shared pool中分配了一个private undo buffer,一方面可以在内存中完成一致读的操作,另一方面,undo信息只在必要情况下批量写入到redo log中,保证数据库crash后可以恢复到一致状态。另外,Oracle总是会尽可能的保留undo buffer中的信息,以便可以在内存中完成一致读的操作,而且undo信息在写入undo block时,Oracle进行了合并处理,减少了undo block的消耗量和对应的redo产生量。
从Oracle 10g开始,引入了private redo strands的概念,在shared pool中分配了一个private redo buffer的空间,每个事务产生的redo都放在这里(9i是放在PGA里面),每个buffer分配一个redo allocation latch,用来解决9i中redo allocation latch的争用问题。其实IMU和private redo strands这个特性是相关的,IMU相当于private undo buffer,当redo strand或者undo buffer空间不足时,会发生IMU flash事件,将redo信息(包括undo)写入到redo log中。

 

IMU实现复杂,在很多情况下Oracle会自动禁用IMU特性,比如RAC和Stream环境。
–EOF–
关于IMU的资料很少,我也是一知半解。我所获取的信息来自于FreePatents上Oracle申请的一篇专利《Method and Mechanism for Implementing In-Memory Transaction Logging Records》,以及《Oracle performance Firefighting》中关于IMU的内容,Tanel Poder的《Performance and Scalability Improvements in Oracle 10g and 11g》,以及我自己的实验和思考。
内容不一定正确,欢迎大家批评指正。

 

原创粉丝点击