相对其它数据库而言Oracle是最早实现CR(consistens read一致读)的RDBMS,以前有同事老是抱怨Sysbase并发性能差,我当时就很困惑,why?后来才明白,sysbase那个版本根本就不支持CR。 CR,简单来讲就是,我查询前,我有一个时间点scn,我的查询只能涉及这个scn之前已经commit的数据,或者在我的session中被我更改的了数据。The other guys?it’s none of my business。这样,就有两个好处: 1.查询不会被其它人阻塞。 2.数据的多版本。 下面,我通过一系列步骤来实验这个过程。 首先,我打开一个干净的数据库(刚启动,没有任何user事务)。我要做实验的表TEST1在文件5,block从9开始,共有8个数据块。从12块开始,是数据,之前9~11是元数据。 (我创建时已经知道) SQL> desc test1;
名称 是否为空? 类型
----------------------------------------- -------- -------------
C1 VARCHAR2(10)
C3 NUMBER(8,2)
C4 DATE
随意插入100行测试数据
SQL> SELECT FILE#,BLOCK#,status FROM v$bh WHERE FILE#=5 order by block#;
FILE# BLOCK# STATUS
---------- ---------- -------
5 2 xcur
5 3 xcur
从这个查询看出,数据块刚刚启动,buffer cache里面只包含一些表空间,数据文件的定义信息。 SELECT * FROM dba_extents s WHERE s.segment_name='TEST1'
在运行了上述语句后,会发现buffer cache里面已经缓存了TEST1的11#block,
SQL> SELECT FILE#,BLOCK#,status FROM v$bh WHERE FILE#=5 order by block#;
FILE# BLOCK# STATUS
---------- ---------- -------
5 2 xcur
5 3 xcur
5 11 cr
11块实际存储的是一些块的元数据信息。
SQL> SELECT T.*,DBMS_ROWID.rowid_block_number(ROWID) FROM TEST1 T;
通过这个查询,可以看出数据块这个一个block 12
C1 C3 C4 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
---------- ---------- -------------- ------------------------------------
200803d4Ad 1 14-3月 -08 12
200803d4Ad 2 14-3月 -08 12
……
查询以后,oralce已经把数据块全部缓存到cache中,包括空数据块。
SQL> SELECT FILE#,BLOCK#,status FROM v$bh WHERE FILE#=5 order by block#;
FILE# BLOCK# STATUS
---------- ---------- -------
5 2 xcur
5 3 xcur
5 11 cr
5 11 xcur
5 12 xcur --有数据
5 13 xcur --空的
5 14 xcur
5 15 xcur
5 16 xcur
已选择9行。
对数据库的结构信息,有大致了解后,我们开始实验。
首先,先设想一下过程,打开一个session A,查询数据,更新所有数据,不提交。打开一个session B,查询数据,它不会被阻塞,而且不会查询到sessionA的修改。提交session A,查询修改后的数据,session B查询修改后数据。
先查询系统,最新的SCN
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
640808
这个scn值是在不停的递增的,可以看做一个序列,因为scn在oracle内部作用非常广泛,所以它随时在变化,当你看到它时候,实际已经不是最新的了。再查一次:
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
640887
下面我们来观察数据块的SCN,有一点肯定,目前数据块的SCN肯定比这个SCN小。
Alter system dump datafile 5 block 12;
Trace文件如下:
Start dump data blocks tsn: 6 file#: 5 minblk 12 maxblk 12
buffer tsn: 6 rdba: 0x0140000c (5/12)
scn: 0x0000.00094acc seq: 0x01 flg: 0x06 tail: 0x4acc0601
frmt: 0x02 chkval: 0x7d42 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
。。。
Block header dump: 0x0140000c
Object id on Block? Y
seg/obj: 0xc890 csc: 0x00.948c1 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1400009 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.017.0000012e 0x008004d8.00ed.18 --U- 100 fsc 0x0000.00094acc
0x02 0x0004.003.00000129 0x0080003b.00fb.0f C--- 0 scn 0x0000.000943c3
data_block_dump,data header at 0x8092264
===============
tsiz: 0x1f98
hsiz: 0xda
pbl: 0x08092264
bdba: 0x0140000c
76543210
flag=--------
ntab=1
nrow=100
frre=-1
fsbo=0xda
fseo=0x378
avsp=0x14fa
tosp=0x14fa
0xe:pti[0] nrow=100 offs=0
0x12:pri[0] offs=0xd23
0x14:pri[1] offs=0xd0a
0x16:pri[2] offs=0xcf1
。。。
可以看出scn: 0x0000.00094acc,并且有两个事务记录。
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.017.0000012e 0x008004d8.00ed.18 --U- 100 fsc 0x0000.00094acc
0x02 0x0004.003.00000129 0x0080003b.00fb.0f C--- 0 scn 0x0000.000943c3
将数据块的SCN转换为10进制的数据为:
SQL> select to_number('94acc','xxxxx') from dual;
TO_NUMBER('94ACC','XXXXX')
--------------------------
608972
和前面猜想一致,小于系统最新的SCN。
打开session A,执行如下语句(no commit)
UPDATE test1 SET c1 = 'aaaaaaaaaa'
观察buffer cache
SQL> SELECT FILE#,BLOCK#,status FROM v$bh WHERE FILE#=5 order by block#;
FILE# BLOCK# STATUS
---------- ---------- -------
5 2 xcur
5 3 xcur
5 11 cr
5 11 xcur
5 12 cr
5 12 xcur
5 12 cr
5 13 xcur
5 14 xcur
5 15 xcur
5 16 xcur
注意到块12,出现了CR块。
观察事务信息
SQL> select ubafil,ubablk,xidusn,xidslot,xidsqn,start_scnb from v$transaction;
UBAFIL UBABLK XIDUSN XIDSLOT XIDSQN START_SCNB
---------- ---------- ---------- ---------- ---------- ----------
2 1481 2 25 313 0
UBAFIL UBABLK
---------- ----------
2 1481
记录的是当前事务在,回滚段里面的文件号和块号。
alter system dump datafile 2 block 1481;
buffer tsn: 1 rdba: 0x008005c9 (2/1481)
scn: 0x0000.0009d775 seq: 0x03 flg: 0x04 tail: 0xd7750203
frmt: 0x02 chkval: 0x130e type: 0x02=KTU UNDO BLOCK
Hex dump of block: st=0, typ_found=1
Scn 大小是:644981,scn已经向前滚动了很多。
********************************************************************************
UNDO BLK:
xid: 0x0002.019.00000139 seq: 0xbf cnt: 0x3 irb: 0x3 icl: 0x0 flg: 0x0000
xid:0x0002.019.00000139和v$transcation是一致的
。。。
col 0: [10] 32 30 30 38 30 33 64 34 41 64
tabn: 0 slot: 81(0x51) flag: 0x2c lock: 0 ckix: 8
ncol: 3 nnew: 1 size: 0
KDO Op code: 21 row dependencies Disabled
xtype: XAxtype KDO_KDOM2 flags: 0x00000080 bdba: 0x0140000c hdba: 0x0140000b
itli: 2 ispac: 0 maxfr: 4858
vect = 0
。。。
Col 0 :这里是undo 块的值,是字段1的旧值。
从之前的dump中可以看到,col 0的旧值就是:32 30 30 38 30 33 64 34 41 64
Update以前的数据块dump:
col 0: [10] 32 30 30 38 30 33 64 34 41 64
col 1: [ 2] c1 5e
col 2: [ 7] 78 6c 03 0e 0e 3a 0c
再次dump 数据块
Start dump data blocks tsn: 6 file#: 5 minblk 12 maxblk 12
buffer tsn: 6 rdba: 0x0140000c (5/12)
scn: 0x0000.0009d775 seq: 0x05 flg: 0x04 tail: 0xd7750605
frmt: 0x02 chkval: 0x868c type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Scn 大小是:644981,注意它和undo块里面scn是完全一致的。
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.017.0000012e 0x008004d8.00ed.18 C--- 0 scn 0x0000.00094acc
0x02 0x0002.019.00000139 0x008005c9.00bf.03 ---- 100 fsc 0x0000.00000000
0x02就是我的未提交事务,注意它的Flag是 ----
tab 0, row 0, @0xd23
tl: 25 fb: --H-FL-- lb: 0x2 cc: 3
col 0: [10] 61 61 61 61 61 61 61 61 61 61
col 1: [ 2] c1 02
col 2: [ 7] 78 6c 03 0e 0e 3a 0c
再看col的值已经变成 61 61 61 61 61 61 61 61 61 61,61就是dec 97,对应的ascii,也就是数字 1。
当前系统scn,这个时候肯定大于数据块的SCN。
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
645791
这个时候说明,修改数据块以后,Oracleoracle要在回滚块中记录老值(前镜像),而实际的data block也被修改了。 这个时候,我们打开session B,运行一次: Select * from test1 。。。 统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
15 consistent gets
0 physical reads
108 redo size
4016 bytes sent via SQL*Net to client
451 bytes received via SQL*Net from client
8 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
100 rows processed
从统计信息上看,出现108 redo size,这是因为该查询会范围回滚段来构造CR块,获得旧的数据镜像,所以会产生redo。多次运行,redo并不会消失,难道每次都从新构建CR块?
将session A,commit,再次在session B 查询,redo 消失。
统计信息
---------------------------------------------------------
0 recursive calls
0 db block gets
14 consistent gets
0 physical reads
0 redo size
4016 bytes sent via SQL*Net to client
451 bytes received via SQL*Net from client
8 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
100 rows processed
再看数据块 alter system dump datafile 5 block 12;
buffer tsn: 6 rdba: 0x0140000c (5/12)
scn: 0x0000.0009dbc2 seq: 0x01 flg: 0x02 tail: 0xdbc20601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Scn 再次更新。同样事务也更新,Flah 变为--U-,commit upper bound
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.017.0000012e 0x008004d8.00ed.18 C--- 0 scn 0x0000.00094acc
0x02 0x0002.019.00000139 0x008005c9.00bf.03 --U- 100 fsc 0x0000.0009dbc2
Oracle在开始一个查询的时候,会去获得一个最新的scn,然后开始遍历buffer cache的链表,找到数据块如果发现block中有未提交事务存在,则去undo里面找前镜像来构建cr块为本次查询服务。如果没有,则比较查询scn是否大于数据块的scn,如果是,则取出数据。否则还是要回滚段去获取。cr就是为维护读一性和多版本,scn和事务槽则是实现的它的重要手段。
原文:http://valen.blog.ccidnet.com/blog-htm-itemid-267367-do-showone-type-blog-uid-51502.html |