ACID特性
oracle如何使用undo和redo来保证了关系数据库的ACID特性。 ACID的特性简单描述为:
- Atomic:以事务为单位的原子性
- Consistency:保证数据一致性
- Isolation:不同事务之间的隔离性,未提交的事务对其它会话是不可见的
- Durablity:提交的事务在系统失败的情况下是可恢复的
oracle使用了redo和undo的机制来完成ACID的特性。
1, Atomic:
当oracle更新数据的时候,会创建undo vector来保存数据的前映像。这样当不同的session访问的时候,使用undo来构建一致性读,最新的数据是不可见的,直到事务提交。
2, Consistency:
数据更新,伴随着oracle的状态变更的整个过程,但对于最终用户来说,只会看到两种状态,老版本和提交后的新版本。而一致性的整个机制依靠undo的完成。但对oralce internal来说,最新的数据是可见的,这也是实现enqueue阻塞的基础。
3, Isolation:
正是由于undo的可用性,保证了不同会话间不会看到更新变更的过程,而只会看到前映像的一致版本或者commit后的最终版本。
4, Durablity:
持久化的特性,oracle使用redo的机制来完成,相比较写更新的数据块到磁盘,在commit的时候,只持久化更新的日志记录就返回完成来说,代价会大大降低,当system发生问题的时候,有这些redo就可以回放重做。 所以,undo和redo的机制完全满足了ACID的特性,不仅仅如此,还实现了performance和recoverability。
5,performance
redo机制实现了oracle 持久化特性的同时,保证了数据更新的异步化,大大提高了事务处理能力。 undo机制实现了oralce事务的隔离,在多用户并发的场景下,实现多版本一致性的基础上,可以实现读写完全不阻塞。大大提高了并发(concurrency)能力
6,recoverability
redo机制记录了oracle数据变更的必要日志,可以使用日志来重新放映数据的变更。
redo:
1,redo的基本机制相对比较简单,顺序的写入变更的日志到buffer中,是事务提交或者其它条件满足的时候,把buffer中redo写入到online redo file中,当一组redo file写完了之后,就switch到下一组,如果oracle设置为归档模式,就会copy redo到archive redo file中,online redo file会被循环使用。
2,通常情况下redo只是写入,copy就完成了,但也会有读的情况,如一些高级特性,logminer,stream,standby等。
3,redo在9i的时候会有一个性能瓶颈,oracle会有一个共享的redo buffer,当会话产生redo change的时候,会写入redo buffer,但在大并发情况下,而oracle分配内存空间时,使用latch机制,这样在高并发高负载的系统中,这个latch(redo allocation)就变成很热的共享资源,会造成不停的重试和sleep。 在10g里,每一个session会有一个private redo buffer and in-memory undo buffer,当一个事务完成时,会一次性的申请public redo allocation latch 来copyr redo到buffer中。这样一个事务获取一个latch,而一个change仅获取一个private redo allocation latch。最后commit时,lgwr获取一个public redo allocation latch。
4,在获得redo allocation latch之前,需要一个redo copy latch,这个latch是为了同步lgwr进程的,即在这个latch过程中,lgwr必须等待,处在log file sync的等待事件中。
undo:
undo record主要完成:
1,read consistency
2,rollback changes
3,delay block cleanout 当开启一个事务的时候,ITL entry里会记录undo的地址,当有新的undo record的时候,就更新itl里的指针,以前的指针会记录在新的undo record中,这样对一个块的更改就形成了一个undo的链表。
下面通过一个实验来看一下:
第一步:dump原始的块的内容:
SYS/SYS@ORCL>alter system dump datafile 4 block 480; Block header dump: 0x010001e0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.029.0000012a 0x0080036a.00f3.05 C--- 0 scn 0x0000.000d6ead
0x02 0x0004.013.000000fb 0x00800054.0110.05 C--- 0 scn 0x0000.000d6a70
0x03 0x0005.00f.00000148 0x0080004c.019f.16 C--- 0 scn 0x0000.000d6ea7
data_block_dump,data header at 0xcc2c27c
................................
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1eb8
0x14:pri[1] offs=0x1e62
0x16:pri[2] offs=0x1e9c
block_row_dump:
tab 0, row 0, @0x1eb8
tl: 14 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [ 7] 78 70 63 68 69 6c 64
tab 0, row 1, @0x1e62
tl: 14 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 03
col 1: [ 7] 78 70 63 68 69 6c 64
tab 0, row 2, @0x1e9c
tl: 14 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [ 7] 78 70 63 68 69 6c 64
第二步:更新一行的记录,观察undo record的地址。
SYS/SYS@ORCL>update test set name='xpchildxpchild' where id=2; Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.029.0000012a 0x0080036a.00f3.05 C--- 0 scn 0x0000.000d6ead
0x02 0x0003.01d.00000135 0x00800021.0162.13 ---- 1 fsc 0x0000.00000000
0x03 0x0005.00f.00000148 0x0080004c.019f.16 C--- 0 scn 0x0000.000d6ea7 data_block_dump,data header at 0xcc2c27c
..................................
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1eb8
0x14:pri[1] offs=0x1e4d
0x16:pri[2] offs=0x1e9c
block_row_dump:
tab 0, row 0, @0x1eb8
tl: 14 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [ 7] 78 70 63 68 69 6c 64
tab 0, row 1, @0x1e4d
tl: 21 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 03
col 1: [14] 78 70 63 68 69 6c 64 78 70 63 68 69 6c 64
tab 0, row 2, @0x1e9c
tl: 14 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 04
col 1: [ 7] 78 70 63 68 69 6c 64
第三步:更新这个块中的第三条记录
SYS/SYS@ORCL>update test set name='xpchildxpchildxpchildxpchildxpchild' where id=3; Block header dump: 0x010001e0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0008.029.0000012a 0x0080036a.00f3.05 C--- 0 scn 0x0000.000d6ead
0x02 0x0003.01d.00000135 0x00800021.0162.14 ---- 2 fsc 0x0000.00000000
0x03 0x0005.00f.00000148 0x0080004c.019f.16 C--- 0 scn 0x0000.000d6ea7 data_block_dump,data header at 0xcc2c27c
..................
0xe:pti[0] nrow=3 offs=0
0x12:pri[0] offs=0x1eb8
0x14:pri[1] offs=0x1e4d
0x16:pri[2] offs=0x1e23
block_row_dump:
tab 0, row 0, @0x1eb8
tl: 14 fb: --H-FL-- lb: 0x0 cc: 2
col 0: [ 2] c1 02
col 1: [ 7] 78 70 63 68 69 6c 64
tab 0, row 1, @0x1e4d
tl: 21 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 03
col 1: [14] 78 70 63 68 69 6c 64 78 70 63 68 69 6c 64
tab 0, row 2, @0x1e23
tl: 42 fb: --H-FL-- lb: 0x2 cc: 2
col 0: [ 2] c1 04
col 1: [35]
78 70 63 68 69 6c 64 78 70 63 68 69 6c 64 78 70 63 68 69 6c 64 78 70 63 68
69 6c 64 78 70 63 68 69 6c 64
最后再看一下undo块的内容
xid: 0x0003.01d.00000135 seq: 0x162 cnt: 0x14 irb: 0x14 icl: 0x0 flg: 0x0000 Rec Offset Rec Offset Rec Offset Rec Offset Rec Offset
---------------------------------------------------------------------------
0x01 0x1f70 0x02 0x1ef8 0x03 0x1e80 0x04 0x1e3c 0x05 0x1dbc
0x06 0x1d5c 0x07 0x1ce4 0x08 0x1c6c 0x09 0x1bf4 0x0a 0x1bb0
0x0b 0x1b30 0x0c 0x1ad0 0x0d 0x1a58 0x0e 0x19e0 0x0f 0x1994
0x10 0x18e4 0x11 0x1880 0x12 0x182c 0x13 0x178c 0x14 0x172c
*-----------------------------
* Rec #0x13 slt: 0x1d objn: 52634(0x0000cd9a) objd: 52634 tblspc: 4(0x00000004)
* Layer: 11 (Row) opc: 1 rci 0x00
Undo type: Regular undo Begin trans Last buffer split: No
Temp Object: No
Tablespace Undo: No
rdba: 0x00000000
*-----------------------------
uba: 0x00800021.0162.10 ctl max scn: 0x0000.000d60d3 prv tx scn: 0x0000.000d6156
txn start scn: scn: 0x0000.000dc9ae logon user: 0
prev brb: 8388655 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04 ver: 0x01
op: L itl: xid: 0x0004.013.000000fb uba: 0x00800054.0110.05
flg: C--- lkc: 0 scn: 0x0000.000d6a70
KDO Op code: URP row dependencies Disabled
xtype: XA flags: 0x00000000 bdba: 0x010001e0 hdba: 0x010001db
itli: 2 ispac: 0 maxfr: 4858
tabn: 0 slot: 1(0x1) flag: 0x2c lock: 0 ckix: 10
ncol: 2 nnew: 1 size: -7
col 1: [ 7] 78 70 63 68 69 6c 64 *-----------------------------
* Rec #0x14 slt: 0x1d objn: 52634(0x0000cd9a) objd: 52634 tblspc: 4(0x00000004)
* Layer: 11 (Row) opc: 1 rci 0x13
Undo type: Regular undo Last buffer split: No
Temp Object: No
Tablespace Undo: No
rdba: 0x00000000
*-----------------------------
KDO undo record:
KTB Redo
op: 0x02 ver: 0x01
op: C uba: 0x00800021.0162.13
KDO Op code: URP row dependencies Disabled
xtype: XA flags: 0x00000000 bdba: 0x010001e0 hdba: 0x010001db
itli: 2 ispac: 0 maxfr: 4858
tabn: 0 slot: 2(0x2) flag: 0x2c lock: 0 ckix: 10
ncol: 2 nnew: 1 size: -28
col 1: [ 7] 78 70 63 68 69 6c 64
总结:itl中的undo block address发生了变更,从0x00800021.0162.13变为0x00800021.0162.14。 在从undo块中,找到slot为ox14的undo record。发现op: C uba: 0x00800021.0162.13,这个代表这link的上一个undo record,找到这个undo block address,验证了我们对undo record link的理解。
checkpoint:
redo的机制保证了数据的更新异步化,但数据更新刷入磁盘的时候是要计入checkpoint点的,这样用redo回放的时候,不用从头开始了。