|
Oracle读一致性
定义
Oracle读一致性是指一个查询所获得的数据来自同一时间点。
分类
Oracle读一致性分为语句级读一致性和事务级读一致性。
|
前提
|
区别
|
语句级读一致性
|
Oracle强制实现语句级读一致性。
|
一个查询语句(select、update、delete)只读取语句开始之前提交的数据。
|
事务级读一致性
|
隔离级别为SERIALIZABLE和read only的事务才支持事务级读一致性
|
事务中的所有查询语句(select 、update、delete)只读取事务开始之前提交的数据
|
语句级读一致性
下面将主要讨论语句级读一致性的实现流程。语句级Oracle 读一致性是由构建CR(consistent read)块实现的,CR块就是传说中的一致性读块,CR块是将undo段中修改前映像(PRE IMAGE)和当前块结合而生成的,并会保存在buffer cache中,因为cr clock和 current block的dba是一致的所以会保存在同一个buffer cache chain中,一个 block的最大CR block大小由隐含参数_db_block_max_cr_dba控制。没构造一个CR块,数据库计数器cr block created就会自动加一。
Oracle在下列很多情况下会生成CR块,下面列出三个比较常见的三种情况:
1、 数据块上有行级锁,并且该锁不是由当前事务加上去的。
2、 打开游标的SCN小于块的SCN。游标打开后数据块被修改。
3、 闪回查询要求查询SCN小于当前块SCN的数据信息。
ORACLE 生成CR块的过程
我们可以对oracle生成CR块的流程进行总结,下图为个人认为的CR生成流程图,当然与oracle生成CR块的实际逻辑相去甚远,仅仅是个人总结,后续试验将进行证明。
流程说明:
1、 查看开始时本例为打开游标时,在PGA中记录打开游标的SCN,假如当前session中有事务则同事也记录XID。
2、 读block到buffer cache中。
3、 分析block的ITL槽,是否有未提交的事务。假如有则执行第四步,假如没有则执行第七步。
4、 假如有未提交的事务,再拿未提交事务的XID与PGA中的XID进行对比。假如相同则执行第七步,假如不相同则执行第五步。
5、 获得未提交事务UBA所指向的undo块,获得undo块中对数据行修改的前映像和对ITL修改的前映像。
6、 将获得的前映像与当前块结合生成临时CR块(该CR块不一定是最终CR块)。
7、 在该步骤中的数据块是没有未提交事务,可能是当前块也可能是临时CR块,获得该数据块的ITL中SCN最大的记录,将该记录的SCN与PGA中查询开始SCN进行对比,假如ITL中的SCN大于查询开始SCN,则继续执行第八步,否则返回该数据库。
8、 从ITL中获得SCN最大的记录,获得UBA,通过UBA获得undo块。
9、 假如undo块已经被覆盖,则返回ora-0155错误。没有被覆盖则继续执行第十步。
10、 获得UBA所指向的undo块,获得undo块中对数据行修改的前映像和对ITL修改的前映像。
11、 将获得的前映像与当前块结合生成临时CR块(该CR块不一定是最终CR块)。
12、 获得该数据块的ITL中SCN最大的记录,将该记录的SCN与PGA中查询开始SCN进行对比,假如ITL中的SCN大于查询开始SCN,则从新继续执行第八步,否则返回该数据库。
语句级一致性读实验数据准备
a) 创建表。
|
scott@ORCL>-- Create table
scott@ORCL>create table T
2 (
3 id VARCHAR2(10),
4 name VARCHAR2(10)
5 );
Table created.
scott@ORCL>
|
b) 插入测试数据。
|
scott@ORCL>insert into t values('1','A');
1 row created.
scott@ORCL>
|
c)
|
scott@ORCL>select t.*,rowid from t;
ID NAME ROWID
---------- ---------- ------------------
1 A AAAS5jAAQAAAACGAAA
|
查看数据行ROWID
查看数据行所在的文件号和块号
|
scott@ORCL>select dbms_rowid.rowid_relative_fno('AAAS5jAAQAAAACGAAA') fileid,dbms_rowid.rowid_block_number('AAAS5jAAQAAAACGAAA') blockid from dual;
FILEID BLOCKID
---------- ----------
16 134
|
可以看到该行数据在16号文件134号块。
SESSION1开启游标
a) 开启游标
|
scott@ORCL>var x refcursor;
scott@ORCL>exec open :x for select * from t;
PL/SQL procedure successfully completed.
|
b) 查看当前SCN
|
scott@ORCL>select T.CURRENT_SCN from v$database t;
CURRENT_SCN
-----------
17163033
|
可以看到当前的scn为17163033。
SESSION2更新数据并提交
a) 执行更新语句
|
scott@ORCL>update t set name='B' where id = '1';
1 row updated.
|
b) 查看数据块ITL
手动设置系统检查点,dump出16号文件134号块。ITL信息如下:
|
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0002.00b.000016de 0x00c01104.02f8.27 C-U- 0 scn 0x0000.0105d3ac
0x02 0x0009.001.000016d2 0x00c00497.03a6.3e ---- 1 fsc 0x0000.00000000
|
请留意这里的Xid(0x0009.001.000016d2),ORACLE构造CR块的时候,会取该事务对应的undo(0x00c01104.02f8.27)块中的前映象与当前数据块组合生成CR块。
而Xid(0002.00b.000016de)会在被覆盖,在构造临时CR块时被从新恢复。
c) 查看undo块信息
从Uba(00c00497.03a6.3e)可以知道undo数据块的地址为3号文件的4356块。Dump该块,查看undorecode信息。
|
Undo type: Regular undo Begin trans Last buffer split: No
Temp Object: No
Tablespace Undo: No
rdba: 0x00000000Ext idx: 0
flg2: 0
*-----------------------------
uba: 0x00c00497.03a6.3b ctl max scn: 0x0000.0105df00 prv tx scn: 0x0000.0105df02
txn start scn: scn: 0x0000.0105e32e logon user: 84
prev brb: 12583150 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x03 ver: 0x01
compat bit: 4 (post-11) padding: 1
op: Z
Array Update of 1 rows:
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 191
ncol: 2 nnew: 1 size: 0
KDO Op code: 21 row dependencies Disabled
xtype: XAxtype KDO_KDOM2 flags: 0x00000080 bdba: 0x04000086 hdba: 0x04000082
itli: 2 ispac: 0 maxfr: 4858
vect = 2
col 1: [ 1] 41
|
在这个undorecode中没有对ITL修改的前映项,因为这个ITL槽第一次被使用。
d) 提交修改
|
scott@ORCL>commit;
Commit complete.
scott@ORCL>
|
SESSION2更新数据不提交
a) 执行更新语句
|
scott@ORCL>update t set name = 'C' where id = '1';
1 row updated.
scott@ORCL>
|
b) 查看数据块ITL
手动设置系统检查点,dump出16号文件134号块。ITL信息如下:
|
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0009.01f.000016cf 0x00c00497.03a6.3f ---- 1 fsc 0x0000.00000000
0x02 0x0009.001.000016d2 0x00c00497.03a6.3e C--- 0 scn 0x0000.0105f2c2
|
可以看到前一个事务(0009.001.000016d2)已经被提交,并且提交的scn为(0105f2c2)。并产生了新的事务(0009.01f.000016cf),原旧事务被覆盖。
c) 查看undo块
从Uba(00c00497.03a6.3f)可以知道undo数据块的地址为3号文件的4356块。Dump该块,查看undo recode信息。
|
* Rec #0x3f slt: 0x1f objn: 77411(0x00012e63) objd: 77411 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: 0x00000000Ext idx: 0
flg2: 0
*-----------------------------
uba: 0x00c00497.03a6.3e ctl max scn: 0x0000.0105df02 prv tx scn: 0x0000.0105df03
txn start scn: scn: 0x0000.0105f30b logon user: 84
prev brb: 12584064 prev bcl: 0
KDO undo record:
KTB Redo
op: 0x04 ver: 0x01
compat bit: 4 (post-11) padding: 1
op: L itl: xid: 0x0002.00b.000016de uba: 0x00c01104.02f8.27
flg: C-U- lkc: 0 scn: 0x0000.0105d3ac
Array Update of 1 rows:
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 191
ncol: 2 nnew: 1 size: 0
KDO Op code: 21 row dependencies Disabled
xtype: XAxtype KDO_KDOM2 flags: 0x00000080 bdba: 0x04000086 hdba: 0x04000082
itli: 1 ispac: 0 maxfr: 4858
vect = 2
col 1: [ 1] 42
|
可以看到undo块中包括了对数据行修改的前映像也包括ITL的前映像。
Session1打印游标
通过print x 看到打印出来的为A,两个事务都没有对其产生影响。
|