Chinaunix首页 | 论坛 | 博客
  • 博客访问: 104594449
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: Mysql/postgreSQL

2008-04-30 15:18:30

相对其它数据库而言Oracle是最早实现CRconsistens read一致读)的RDBMS,以前有同事老是抱怨Sysbase并发性能差,我当时就很困惑,why?后来才明白,sysbase那个版本根本就不支持CR
CR,简单来讲就是,我查询前,我有一个时间点scn,我的查询只能涉及这个scn之前已经commit的数据,或者在我的session中被我更改的了数据。The other guysit’s none of my business。这样,就有两个好处:
1.查询不会被其它人阻塞。
2.数据的多版本。
下面,我通过一系列步骤来实验这个过程。
首先,我打开一个干净的数据库(刚启动,没有任何user事务)。我要做实验的表TEST1在文件5block9开始,共有8个数据块。从12块开始,是数据,之前911是元数据。
(我创建时已经知道)
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里面已经缓存了TEST111block

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值是在不停的递增的,可以看做一个序列,因为scnoracle内部作用非常广泛,所以它随时在变化,当你看到它时候,实际已经不是最新的了。再查一次:

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 大小是:644981scn已经向前滚动了很多。

********************************************************************************

UNDO BLK:

xid: 0x0002.019.00000139 seq: 0xbf cnt: 0x3   irb: 0x3   icl: 0x0   flg: 0x0000

xid0x0002.019.00000139v$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 6161就是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 Acommit,再次在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

阅读(350) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~