事务和一致性
oracle的redo和undo机制保证了数据库的ACID特性,以及高性能和可恢复特性。
redo的数据是记录着数据块变更的顺序的正向数据流,
- commit时,保证redo同步持久化,保证高性能。
- 恢复的时候,顺序的应用日志。
redo的机制相对比较简单。
undo记录着数据块的前映像。
- 保证事务的特性,数据只有在commit后才可见。
- 提供读一致性,保证读写互不阻塞。
一方面,在undo segment header中transaction table中记录着事务,并在undo的record中维护着link list,实现事务的回滚。
另一方面,在数据块中intereted transaction list中保存着本事务对数据块的前映像的最新UBA,并在undo recored中
维护着link list,用于构建数据块的一致性读。
在正常的业务处理中,rollback属于比较少的情况,所以,oracle也特别优化了机制,commit的时间会非常的快,而rollback
因为要根据linked list顺序应用undo record,要比commit慢的多。
undo的机制相对比较复杂,这里就从两个不同的立足点去讨论
- 事务
- 表或者索引的数据块
1,事务
undo segment header包括了一般的数据块的通用结构,还包括了特殊的结构,
比如transaction table, extent retention table。
在8k大小的数据块中,每个undo segment header中的transaction table是有限的。比如下面的片段:
TRN TBL:: index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt
------------------------------------------------------------------------------------------------
0x00 9 0x00 0x00f9 0x0011 0x0000.000e3357 0x008002be 0x0000.000.00000000 0x00000001 0x00000000 1326654543
0x01 9 0x00 0x00f9 0x0000 0x0000.000e3297 0x008002be 0x0000.000.00000000 0x00000001 0x00000000 1326654005
0x02 9 0x00 0x00f9 0x0006 0x0000.000e2fe0 0x008002bd 0x0000.000.00000000 0x00000001 0x00000000 1326652743
0x03 9 0x00 0x00f9 0x0008 0x0000.000e3153 0x008002be 0x0000.000.00000000 0x00000001 0x00000000 1326653343
0x04 9 0x00 0x00f9 0x002d 0x0000.000e30f3 0x008002bd 0x0000.000.00000000 0x00000001 0x00000000 1326653343
0x05 9 0x00 0x00f9 0x000c 0x0000.000e312a 0x008002be 0x0000.000.00000000 0x00000001 0x00000000 1326653343
.......
当事务为inactive状态,并且slot被重用的情况下,wrap#会向上递增。
这里有一点要注意:在oracle内容,持久化到文件的变更,db的restart都不会重置,而对于内存中的记录,比如动态性能视图,会被清除重置。
下面记录下事务的开始和结束进行的action:
1,begin transaction
2,写undo数据块变更的redo日志到redo buffer
3,变更undo数据块,包括,选择undo segment,transaction table entry,递增wrap#,更新state为10,即active。
4,commit 事务
5,写undo变更的redo
6,更新state为9,写入当前的scn到scn column中
7,同步等待lgwr写入完成
这里仅仅记录了在事务中,undo变化的过程。
注:在commit的时候,产生的所谓“redo commit record”,其实是undo segment header数据块变更的redo record。
在对事务的标示时,使用的是transaction id,这里唯一标识了一个事务,例如:0x0009.002.00002013
其包括:undo segment, entry,wrap#三部分。
所以,以事务为单位,在transaction table中可以看到以下信息:
1,事务(transaction id)的物理地址
2,事务的状态
3,事务提交的scn号
4,事务的最新的undo记录的地址
5,事务的undo记录量
查询这些信息不需要dump块,这里有一个视图x$ktuxe,但这个视图会扫描所有的undo segment header块。
select
indx,
ktuxesta,
ktuxecfl,
ktuxesqn wrap#,
ktuxescnw scnW,
ktuxescnb scnB,
ktuxerdbf dba_file,
ktuxerdbb dba_block,
ktuxesiz nub
from
x$ktuxe
where
ktuxeusn = 9
and ktuxeslt <= 5
注意:在oracle的官方文档中有一句“transactions don’t share undo blocks”,这里的意思是,这个事务own这个undo块,但如果这个事务是inactive的时候,
如果还有空闲空间,可是会被其它事务所使用。当undo要被清除的时候,oracle开辟一个块大小的buffer,然后format,而并非是从文件中读取块的内容。
2,表或者索引的数据块
下面看一下构建一致性块的过程
主要存在以下两种情况:
1,当事务未commit状态的时候,使用undo信息构建一致性块内容。
2,当块中事务commit的scn比当前query的scn大,使用undo 信息构建一致性内容。
这其中存在延迟块清除的情况:即当commit的时候,为了加快commit的时间,当事务更新的块较多的时候,仅仅更新了undo segment header中的transaction table中记录的事务的状态,当后面query到这个块的时候,进行块清除,即更改itl中事务的状态。
所以构建的主要过程:
1,在buffer中克隆这个块,下面的步骤都将应用在这个块上。
2,对这个块上所以commit的事务,进行块清除。
3,回滚没有提交的事务,应用undo record。
4,当commit的事务的scn大于query的scn时,就应用undo record。
5,重复第四步。
3,commit scn
fast commit:当事务commit的时候,仅仅修改transaction table的undo segment header这一个块,而跟修改的数据块的数量没有关系,修改这个块的redo vector就是所谓的commit record。
在oracle的演化过程中,oralce改变了fast commit,session memory中保存着(最多10% buffer cache)更改的块的list,当commit的时候,就会访问这些块,进行块清除。主要包括:
1,清理lock bytes
2,设置fsc/scn为commit的scn
3,设置commit的flag为C----
这个过程称为commit cleanout。
注:在这个过程中,仅仅更改transaction table slot中产生了redo日志,oracle优化了commit cleanout的过程。
其中,块的revisit并没有计入logical read,redo日志。
总结:
即:在事务的整个过程中,有两个重要的结构
1,itl
itl记录着事务的id,即txid,undo记录的地址UBA和commit scn,如果commit scn不可用,那么可以根据txid来确定undo segment header中的transaction table
slot中的信息,确定事务的状态,如果要构建一致性块,就根据UBA的地址找到undo record的链,回滚一致性的块内容。
2,transaction table
slot中记录着transaction的状态,最后更新的块的undo地址和commit scn,slot是有限的,当被重用的时候,会递增wrap#,wrap#数值是组成txid的一部分。
当事务回滚的时候,根据UBA地址,然后再undo record中有pointer指向想一个UBA,以此来回滚整个事务。
当slot被重用的时候,前一个事务的commit scn,uba地址会记录在下一个新事物的第一个undo record中,这样,就可以构建一致性的transaction table。
当事务根据UBA的链表查找的时候,发现当前undo record的seq#数值与当前不对,就会报出“ORA-01555”错误,说明undo块已经被重用。