Oracle学习笔记 深入剖析事务槽及Oracle多种提交方式

Oracle 学习笔记

深入剖析事务槽及Oracle多种提交方式

这节课把事务槽和oracle事务的提交方式讲一下
讲完以后再去回顾上节课讲的oracle的undo里面的事务的整个操作过程的时候大家就更清晰了

一)事务槽数量参数

每一个oracle数据块里面在数据块的头部都有事务槽

事务槽的数量可以去查一下

比如数据库中有一个表t2,可以查一下这个表的事务槽的数量

SQL> select INI_TRANS,MAX_TRANS from dba_tables where table_name='T2';

 INI_TRANS  MAX_TRANS
---------- ----------
         1        255
         1        255

这个表t2事务槽的数量
默认是INI_TRANS的值1,表刚建立的时候默认值是1
最大是MAX_TRANS的值255

oracle中INI_TRANS和MAX_TRANS这两个参数属于单个表
在系统参数和隐含参数中都没有这两个参数
oracle从10g开始最大的那个MAX_TRANS的值不能改了
不管改成多少最终值总是255

默认的数量INI_TRANS可以改

因为这个属性属于单个表,
可以在建表的时候指定值,也可以在建表后使用修改语句修改表的这个属性

INI_TRANS指这个表分配新数据块时,数据块有事务一开始数据块就拥有的事务槽的数量
可以在建表时的语句中指定值,默认值是1,
这样这个表分配的数据块初始时就有INI_TRANS值的事务槽
也可以在建表后使用alter table命令修改
如:ALTER TABLE T10 INITRANS 4;
修改后这个表
原来已有数据块的初始事务槽数量仍保持原值
修改后新建的数据块的初始事务槽数量就是修改后的值

这是关于事务槽的数量
一般我们不去改,默认是1最大是255

二)事务槽争用

一个数据块里面放了很多行
底下有很多的数据行
上面开始默认一开始有1个事务槽

当要修改数据块的时候
修改包括增加、删除和更新
不管哪个操作要对这个块进行操作的时候
首先要在这个块获取到一个事务槽
将事务信息将xid写上
同时还要写uba地址就是回滚块的地址

数据块中事务槽的xid指向事务表,uba指向回滚块

然后对数据块进行操作进行修改
修改了以后把修改前的数据写到回滚块里面去

当一个事务A事务对这个数据块进行操作的时候
获取到一个事务槽

一个事务可以在数据块中修改多行
但只需要一个事务槽就可以

有可能另外一个事务B事务也要对这个数据块进行修改
这时候它需要获取第二个事务槽

事务槽是以事务为单位的

A事务在数据块里面获得事务槽以后修改了几行,一直没有提交
没有提交这个事务槽就不能被覆盖

只有这个事务被提交以后这个事务槽才能被覆盖
这个点很关键

B事务上来后也要分一个事务槽

C事务也要修改这个数据块的话也要分配一个事务槽

一个数据块我们知道有一个pct free 10%

数据块上面是块头的事务槽
底下是数据行
中间留10%的空间

留10%是为了将来
1、我更新底下的数据的时候数据可能往上占空间
2、当有多个事务同时修改块的时候需要增加事务槽的数量,块头的数据要往下压

增加数据行往上压,增加事务槽往下压
10%的空间是预留给这个用的

有可能出现这个情况
多个事务同时修改这个数据块
产生了非常多的事务槽
这个时候我们发现这里面只能搁四个事务槽,再往下就没空间了
第5个事务再要修改这个数据块的时候
它就会要等待前面的四个事务槽去提交

这叫事务槽的争用ITL争用
事务槽英文叫ITL(Interested Transaction List)
ITL是Oracle数据块内部的一个组成部分,位于数据块头

如果事务槽的争用这种情况发生可以把pct free增加一些

比如有一个批量的插入操作
表t分配一个区,这个区有八个块
有三个事务同时往t里面做insert做插入操作

而一个数据块默认有1个事务槽

如果说这三个事务同时往一个块里插入的话
这时oracle需要额外的增加两个事务槽
在原有有一个事务槽的基础上在一个数据块增加两个事务槽
就会额外的占用空间

针对这种情况
往一个表中多个事务并行插入数据的时候

第一个事务使用一个块
第二个事务我优先使用第二个块
第三个事务优先使用第三个块

oracle为了避免事务槽的争用
尽量的对insert操作分布到多个块里去,减少事务槽的争用问题

但是对于delete和update往往就无能为力了
一个事务更新的这行就在这个块里面
另外一个事务更新的这行也在这个块里面
第三个事务也在这个块里面
就没办法了

更新和删除
往往是针对某些具体的行进行更新和删除
这个时候这个行就在这个块里面
这个事务没有办法就必须增加事务槽

容易发生事务槽争用的主要集中在update和delete上

三)dump事务槽

1)准备和收集信息

我们建一个表
然后把事务槽给dump出来来看一下

建一个表

SQL> create table t10(id number(5),name char(2000));

Table created.

SQL> desc t10;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                                 NUMBER(5)
 NAME                                               CHAR(2000)

然后insert数据

SQL> insert into t10 values(1,'aa');
insert into t10 values(2,'bb');
insert into t10 values(3,'bb');
insert into t10 values(4,'cc');
insert into t10 values(5,'dd');
commit;
1 row created.

SQL>
1 row created.

SQL>
1 row created.

SQL>
1 row created.

SQL>
1 row created.

SQL>

Commit complete.

提交了

执行select * from t10;
结果有数据
name列比较长总体结果显示的有些乱
这里不用看这个语句的结果,就不列出来了

然后执行

SQL> select dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid),id from t10;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)         ID
------------------------------------ ------------------------------------ ----------
                                   1                                60786          1
                                   1                                60786          2
                                   1                                60786          3
                                   1                                60787          4
                                   1                                60787          5

有5行数据

ID为1、2、3、4、5,对应原表t10中的ID列
id为1的为第一行

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID)为所在文件号
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)为所在块号

第1行ID为1的行在1号文件的60786块

这五行占了两个块
文件号都一样,块是60786和60787两个块

然后更新一下t10表
更新是一个事务

SQL> update t10 set name='abcd' where id=1;

1 row updated.

更新以后去查一下

SQL> select ubafil,ubablk,xidusn,xidslot,xidsqn,start_scnb from v$transaction;

    UBAFIL     UBABLK     XIDUSN    XIDSLOT     XIDSQN START_SCNB
---------- ---------- ---------- ---------- ---------- ----------
         2        750          8         24        422          0

建一个表插入数据提交
然后对这个表又做了更新
更新了一行就更新了一个数据块

UBAFIL和UBABLK
代表着回滚块信息
XIDUSN 和XIDSLOT 和XIDSQN
是 回滚段的编号、事务表的编号、还有被覆盖多少次

前面的UBAFIL UBABLK XIDUSN XIDSLOT XIDSQN
这5个信息从事务表来的

一个事务在事务表中有xid还有指向uba
它里面查的xid包含
XIDUSN段号、XIDSLOT事务表里的槽位号、XIDSQN被覆盖次数
就是xid分解出来的
UBAFIL、UBABLK这两个信息就是指向的uba,uba的地址

2)dump回滚段头块

这时候回滚段的编号知道了
这里XIDUSN的值是8号回滚段

编号知道了就可以把段的名字找出来

SQL> select * from v$rollname;

       USN NAME
---------- --------------------
         0 SYSTEM
         1 _SYSSMU1$
         2 _SYSSMU2$
         3 _SYSSMU3$
         4 _SYSSMU4$
         5 _SYSSMU5$
         6 _SYSSMU6$
         7 _SYSSMU7$
         8 _SYSSMU8$
         9 _SYSSMU9$
        10 _SYSSMU10$

11 rows selected.

结果中8号段名字为_SYSSMU8$

知道这个段以后

alter system dump undo header ‘_SYSSMU8$’;
可以把段头给dump出来

SQL> alter system dump undo header '_SYSSMU8$';

System altered.

然后找一下当前会话的编号

SQL> select spid from v$process where addr in (select paddr from v$session where
        sid=(select sid from v$mystat where rownum=1));  2

SPID
------------
6066

执行dump命令的会话的编号为6066

可以去找那个文件了
找到文件6066trace文件可以vi看了

3)dump回滚块

当然还可以dump块

    UBAFIL     UBABLK 
---------- ---------- 
         2        750

2号文件的750块

使用命令
alter system dump datafile 2 block 750;
就可以把这个回滚块dump出来

回滚段的段头块可以dump出来
回滚段的回滚块可以dump出来

4)dump数据块

还可以dump id等于1的数据行所对应的数据块

我们查一下id等于1的行所在数据块是谁

SQL> select dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid),id from t10;

DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)         ID
------------------------------------ ------------------------------------ ----------
                                   1                                60786          1
                                   1                                60786          2
                                   1                                60786          3
                                   1                                60787          4
                                   1                                60787          5

结果中id等于1的数据行所在的数据块为1号文件的60786数据块

alter system dump datafile 1 block 60786;
就可以把这个数据块dump出来了

先退出会话再重新建立会话以得到不同的进程编号

SQL> alter system dump datafile 1 block 60786;

System altered.

这个dump出来的就是数据块的数据,就是数据块的具体的信息

然后具体的查一下执行dump的进程是哪个进程编号

SQL> select spid from v$process where addr in (select paddr from v$session where
        sid=(select sid from v$mystat where rownum=1));  2

SPID
------------
7519

由于改变了会话,进程编号和上面的不一样了,现在为7519

去系统目录找一下得到的trace文件

[oracle@redhat4 udump]$ pwd
/u01/app/oracle/admin/jiagulun/udump
[oracle@redhat4 udump]$ ls
jiagulun_ora_10692.trc  jiagulun_ora_13841.trc  jiagulun_ora_24967.trc
jiagulun_ora_11994.trc  jiagulun_ora_17335.trc  jiagulun_ora_27148.trc
jiagulun_ora_12910.trc  jiagulun_ora_18203.trc  jiagulun_ora_27861.trc
jiagulun_ora_13393.trc  jiagulun_ora_19827.trc  jiagulun_ora_29885.trc
jiagulun_ora_13449.trc  jiagulun_ora_20062.trc  jiagulun_ora_30323.trc
jiagulun_ora_13450.trc  jiagulun_ora_2020.trc   jiagulun_ora_32457.trc
jiagulun_ora_13475.trc  jiagulun_ora_20217.trc  jiagulun_ora_3940.trc
jiagulun_ora_13525.trc  jiagulun_ora_20471.trc  jiagulun_ora_3944.trc
jiagulun_ora_13529.trc  jiagulun_ora_20922.trc  jiagulun_ora_4044.trc
jiagulun_ora_13547.trc  jiagulun_ora_21317.trc  jiagulun_ora_6066.trc
jiagulun_ora_13550.trc  jiagulun_ora_21344.trc  jiagulun_ora_6443.trc
jiagulun_ora_13551.trc  jiagulun_ora_21549.trc  jiagulun_ora_6616.trc
jiagulun_ora_13567.trc  jiagulun_ora_21871.trc  jiagulun_ora_6643.trc
jiagulun_ora_13592.trc  jiagulun_ora_21898.trc  jiagulun_ora_6644.trc
jiagulun_ora_13617.trc  jiagulun_ora_21901.trc  jiagulun_ora_7317.trc
jiagulun_ora_13633.trc  jiagulun_ora_21929.trc  jiagulun_ora_7519.trc
jiagulun_ora_13660.trc  jiagulun_ora_21978.trc

jiagulun_ora_7519.trc文件就是dump出来的文件

数据块内容很多

信息中有事务槽部分

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0001.017.00000147  0x0080000a.0101.33  C---    0  scn 0x0000.000c6571
0x02   0x0008.018.000001a6  0x008002ee.0114.0e  ----    1  fsc 0x0000.00000000

其中

0x02   0x0008.018.000001a6  0x008002ee.0114.0e  ----    1  fsc 0x0000.00000000

为我们使用的新的事务槽

0x0008.018.000001a6为事务id
0x008002ee.0114.0e为uba
Flag为是否已提交标志
Lck为锁标记
等等

5)dump总结

本节讲了三种块的dump方式,包括undo段段头块,回滚块,普通数据块

总结如下:

1、事务开始后,要得到这个事务的xid和uba信息
select ubafil,ubablk,xidusn,xidslot,xidsqn,start_scnb from v$transaction;
这些信息中包含该事务使用的回滚段和回滚块的信息
dump出段头块和回滚块要依此开始

2、dump出回滚段头块
根据事务信息找到事务使用回滚段的编号,依此找到回滚段的名字
select * from v$rollname;

以回滚段名dump
alter system dump undo header ‘_SYSSMU1$’;

也可以以回滚段名找到回滚段头块的地址包括文件名和块号
select header_block,header_file from dba_segments where segment_name=’_SYSSMU1$’;
使用dump出数据块的方式得到段头块
alter system dump datafile 2 block 9;

dump undo header方式得到段头块信息文件,解读了文件中所有的信息
dump datafile方式段头块中的有些信息没有解读,直接以二进制方式提供出来了

3、dump出回滚块
事务信息中的ubafil、ubablk代表了事务使用的回滚块的文件号和块号
依次可dump出回滚块
alter system dump datafile 2 block 750;

4、dump出普通数据块

先得到普通表中每条记录对应的数据块
select dbms_rowid.rowid_relative_fno(rowid),dbms_rowid.rowid_block_number(rowid),id from t10;
依此dump出普通数据块,这种方式可以精确到一条记录所在的数据块

alter system dump datafile 1 block 60786;

也可以先得到数据段的第一个块的地址
select header_file , header_block from dba_segments where segment_name=’T10’;
或者根据
select * from dba_extents where segment_name=’T10’;
得到段的所有区块的统计信息
然后dump出这个段的其中的一个数据块

5、查看dump出来的数据块内的信息
得到执行dump命令的进程的id
select spid from v processwhereaddrin(selectpaddrfromv session where
sid=(select sid from v$mystat where rownum=1));
默认dump出的文件在/u01/app/oracle/admin/jiagulun/udump目录下
文件名中包含上面得到的serverprocessID
找到文件然后vi看

vi jiagulun_ora_21978.trc

四)详解ITL

一个事务槽例子

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x000a.012.0000027a  0x00800702.012d.07  C---    0  scn 0x0000.001f45ce
0x02   0x0008.008.000002e9  0x0080238e.02c7.22  C---    0  scn 0x0000.001f45c7
0x03   0x0009.026.00000313  0x00800044.0166.0c  ----    7  fsc 0x0031.00000000

这是展示的一个事务槽
是讲课老师做过的做出来的一个事务槽

刚才演示了一系列操作
就是如何找到数据块,如何找到undo段头,如何找到undo块
把这三个找到
然后如何dump出来然后vi去研究

我们看事务槽,事务槽里面有哪些信息

第一列为事务槽编号Itl编号

还有事务id(transaction identifier)即Xid
事务id指向具体的事务,并指向回滚段段头块里面的事务表里面的记录
格式:xidusn.xidslot.xidsqn
分为三部分
包括:这个事务使用的回滚段号.在回滚段头事务表中的记录位置.这个位置已被覆盖的次数

uba指向具体的回滚块中的记录
格式:uba(undo block address).UBASQN.UBAREC
地址也分三段
第一段:回滚数据块的地址,就是在回滚段内的块号
第二段:undo块被重用的次数,第三段:在undo块的第几条记录

Flag为事务标志位,记录了这个事务的状态,如事务是活动的还是已提交等

Lck为这个事务拥有的锁的数量,也可理解为影响的记录数

Scn/Fsc提交方式为普通提交时标记为scn,为快速提交则先标记为fsc,
然后后面都再跟上scn值就是提交时的一个时间点

undo段头事务表里面也类似有这些信息
一个事务即在事务表里有信息在事务槽里面也有信息

五)数据块中存放事务信息的意义

一个事务开始以后
首先在回滚段,回滚段头的事务表里面找到一个事务槽
同时分配一个回滚块
事务表指向回滚块
事务表中这条信息有xid、是否已提交标志

然后修改一个数据块
数据块分配一个事务槽
事务槽指向undo段头事务表中的一条事务记录

数据块事务槽的uba指向的是对应的undo块
事务槽还有是否已提交标记

然后在数据块中修改数据行
数据行行的头部指向数据块的事务槽

修改了三个数据行
这三个数据行的头部都指向数据块头部的这个事务对应的事务槽

我要修改一个数据块首先在事务槽分配事务

如这个事务的事务槽是1号事务槽
这个事务修改了三行
在这个事务每个被修改的行的头部都写上1
就是都指向1号事务槽

一个事务正在修改这个数据块
事务的信息在事务槽里面写了

这个事务修改了3行
这三行都指向事务槽
槽里面有事务提交标志

另外一个事务再来修改这个块的时候
它又获取另外一个事务槽,如获取了2号事务槽

二号事务也可能要修改一号事务修改的一个数据行
当他修改数据行的时候,发现这个数据行的头部写的1
它就知道目前有一个事务,1号事务槽的事务正在修改这个行

对这个行,1号事务有两种情况
第一种情况已提交,第二个情况未提交

如果已提交的话
对应事务的事务槽会写了已提交标志
它就可以覆盖这个行了

如果他发现事务槽中的已提交标志没有写的话
它就认为对应的这个行还没有提交
没有提交的话两个事务不能同时修改一个行

在一个数据块里面
有事务信息及有一个关于一个事务是否已提交的标志是有意义的

假设在这个数据块里面没有这个信息
也就是说对oracle数据库来讲,关于某个事务只有回滚段段头有
这时我要读这个数据块的时候,要修改这个数据块的时候
要修改一行的时候
这个行到底有没有被事务修改只能查undo段头里的事务表
查它才能知道这个行被谁修改了、事务有没有提交
所有的修改都会查回滚段段头
这样的话会出现段头的争用,不现实

这就是为什么我要把事务信息
即写在回滚段段头里面,也要写在每个数据块的事务槽里面
就是因为
我将来访问数据块某个行的时候
我要看这个行有没有被修改
有没有被事务修改、被哪个事务修改、修改它的事务有没有提交
这个信息在本身这个数据块里就有
直接查就完了,不需要再去查回滚段段头块了

事务信息在两个地方都有是有意义的

六)快速提交

1)产生原因

这个地方有个问题涉及到oracle的一个提交方式

假设一个事务修改了1000个块
修改了一千个块以后要提交
提交以后这个事务必须把事务的提交标志置成已提交

一个事务修改了1000个块
意味着关于这个事务的信息在1001个地方有
因为每个数据块里面有事务信息,还有undo段头有事务信息
在1001个地方有这个关于事务的信息

我提交了,在1001个地方把事务提交的状态都给它写上
会出现commit提交的速度会很慢,因为要访问1001个块

况且还有另外一种情况

修改了一千的块,这个事务持续的时间很长
在这个过程里面
这1000个块里面其中有800个块已经写到磁盘了
因为dbwr写的时候就什么也不管,它不管你有没有提交
写到磁盘,内存中原有块就是干净块了
干净块就可能被覆盖

也就是说我提交的时候
我发现修改了1000个块
只有200个块或者只有300个块在内存里面
还有另外700个块在磁盘上
为了提交这个事务
还有把那700个块读到磁盘
如果是这样的话提交速度会很慢

我们把事务信息写到数据块里面和undo段里面,写两份是有好处的

将来oracle更新数据行的时候
找这个数据行对应的事务信息的时候从块找就可以很方便

但是带来另外一个问题
oracle提交的时候要过多地在很多地方修改事务信息
这时候带来提交慢

所以说为了解决这个问题
oracle推出一个技术叫快速提交

2)原理

oracle提交的时候
如果发现这个事务这次所修改的数据块过多
它会只是更新undo表空间里面的事务信息
只把undo段头里的事务信息更新了
数据块里的那些事务槽,它不更新或者更新少量

这样提交速度会很快
也就是说回滚段段头里的事务一定最能准确的代表着这个事务是否已提交
但是普通数据块里面的事务槽的信息,那里面是否已提交不怎么准确

如果数据块数据槽这里面写了已提交那肯定已提交了
如果写了未提交就不见得未提交

3)举例

回滚段里有事务信息
数据块里有事务信息

数据块里有三个行被修改了
数据块上面有事务槽

事务信息在所修改数据块里有
在回滚段里也有

oracle要修改数据块里一个行
另外一个事务也要修改这个行
它也占用一个事务槽

但被修改行的事务信息指向第一个事务槽
就意味着
第二个事务要修改这个行
这个行又指向第一个事务的事务槽

第二个事务就读一下第一个事务槽
看一下第一个事务对应的事务槽
看一下第一个事务是否已经提交

如果已提交的话
第二个事务就可以直接修改这个数据行
因为已提交肯定是准确的

如果他发现这个事务未提交
它就怀疑了
怀疑这个第一个事务槽所对应的事务不一定未提交
有可能已提交了当时这个提交的时候没有清理
要去查undo段头的事务表
查事务表里面的事务槽

在数据块的事务槽有个xid
通过xid找到事务表

数据块事务槽指向undo段头事务表这时有意义了

一查undo段头的事务表这个事务已经提交了

第二个事务会相信undo段头的事务表而不相信数据块中的事务槽

这时它不但更新要修改的行
这行更新以后这个数据行指向第二个事务在数据块中的事务槽
而且
它一块儿把第一个事务对应数据块中事务槽的事务状态置成已提交

第二个事务做很多事情
帮第一个事务扫扫尾
在一次访问里面无所谓

七)事务信息的指向总结

undo段头事务表 数据块事务槽 和 回滚块 的关系

1、我们讲了undo段头事务表指向这个事务最新undo块,并且对应的所有undo块是串起来

2、数据块头部事务槽直接指向这个事务对这个数据块最新修改对应的最新回滚块

3、数据块的事务槽也指向undo段头事务表,和提交方式有关系

这三个点很重要,都有意义

八)行级锁和事务信息的清理关系

我们看oracle的提交分几步

最彻底的提交
undo段头事务表肯定被修改
数据块中对应的事务槽也被修改
事务槽对应的数据块数据行里面的标记也被修改

如果说提交而提交地最彻底的话
undo段头事务表被修改
事务槽里面的事务状态被修改了
同时数据行前面的事务槽标志也被修改

如果数据行这个标志是空的,这个行没有被修改
这个数据行被修改了,
它就写一个事务槽的标记指向数据块的事务槽
相当于我在这个数据行加了一个锁

一个事务修改这个数据块
获得一个事务槽1号事务槽
修改这个数据行
就在这个数据行的前部加一个数字1指向事务槽1
相当于在这个数据行加了一个锁

第二个事务要修改这个数据行的时候
一看上面有一个锁
它就根据锁找到数据块中锁对应的事务槽
看它是否已提交
如果事务没有提交的话,它不能修改这个行

在这个数据行的前面加了一个事务槽的编号就相当于加了个锁
这就是oracle里面非常有名的叫行级锁
在行的级别上加了一个锁
这个开销不大,直接加个数字就行了
这就是oracle的行级锁

oracle和sqlserver不一样
sqlserver是修改数据块的时候在块上加一个锁叫块级锁
不能两个事务同时修改一个块
但是oracle可以,因为oracle是行级锁
两个事务可以同时修改一个数据块,只要行不冲突就行
oracle的并发性好一些

所以说sqlserver是块级锁,oracle是行级锁
行级锁并发性好一些,而且又没有多大的开销很灵巧

所以说对oracle来讲最彻底的提交方式
undo段头事务表、数据块中的事务槽还有数据行的锁标记
如果这三个都清了,那是最彻底的提交方式

但是oracle如果修改的数据块过多
它会只清undo段头和少量的数据块

一般的情况它第一次提交的时候只会清undo段头事务表和事务槽
有时候数据行的锁定标记经常不清
访问的时候再清一次
这就看这次事务修改多少块了
这就是我们讲的oracle的一个工作机制

九)行锁、事务槽、事务表中事务的提交状态关联作用

1)行锁未清、事务槽标记已提交

oracle的select查询有时候也会产生redo,很奇怪

我们知道只有增删改产生redo
我们来看查询产生redo的原理

oracle事务有undo段头、数据块事务槽、数据行、回滚块
数据行指向事务槽
事务槽指向undo段头事务表
数据块事务槽直接指向回滚块

举个例子看oracle的查询

oracle的select要访问一个数据块
访问这个数据块的一行
访问这个行的时候发现这个行的前面有锁定标记,就是行有锁
说明有可能某个事务正在修改这个块

这个select不能直接查询
要根据数据行这个锁定标记找到对应的事务槽
它发现事务槽里面有信息
事务槽里的事务是否已提交的信息
有可能这个事务已提交但是没有清锁
这时select就直接读这个行
但是这个select会把这个锁标记给它去了
就是把原来的那个数据行指向事务槽的链打断

select做了一件事情
查这个行的同时把这个行的锁也清了
因为它发现这个行所对应的事务已提交了
只是锁倒是没有清,它帮他清了

select读行,但是结果修改了行
所以有时select也会产生日志

2)锁未清、事务槽标记未提交、事务表标记已提交

select还有一种情况
读行时行有行锁

它就找这个事务对应事务槽
它发现事务槽没有提交
没有提交就有可能是这个事务有可能已提交了

select就读事务表
事务表绝对准确
在事务表发现这个事务已提交了
它就把数据块中的事务槽也给清了

这时又产生日志了

3)都未提交,构造cr块

它发现数据行有锁
数据块中事务槽说事务没提交
查undo段头事务表也未提交
这个数据行这时候确实正在被某个事务修改着

修改着时select不能查没有提交的事务
它根据锁找到事务槽
再从事务槽找到undo块
把这个undo块和另外两个没有提交的行构成一个新的块

从锁标记找事务槽再找回滚块
两个没有修改的行,加上这个修改的行
三个行拼起来组成一个cr块
然后直接读这个cr块
这就是一致性读

这个读没有出现脏读
它读的时候没有读未提交的事务

它读的是上一个已提交事务

这就是select的一个读的过程

4)事务表中这个事务没了

另外一种情况以后还会碰到

我们知道一个数据块
在磁盘里面很久没有读出来了

这个数据块的一个行有锁标记
通过锁标记找到事务槽对应的事务
这个事务写着没有提交

假设这个块是昨天修改
结果块被写到磁盘了,结果再也没有访问过

然后我又根据事务槽里的xid去事务表里面去找

xid有三块组成
一块是段号一块是槽号再有被覆盖了多少次

假设段号是8号,在段第15个槽,被第13次覆盖
结果找的时候发现
这个8号段的15号这个槽位已经被第15次覆盖了

我要找8号段的15号槽位的第13次覆盖所对应的事务是否已提交
但是当我找到的时候发现它已经是被第15次覆盖了

因为这个块已经很长时间没有被读出来了
事务槽已经被很多次覆盖了
这个时候
oracle不会报错
oracle就会非常果断地认为那个事务已提交了

如果没有提交的话
事务表中的事务槽不可能被覆盖
既然它都被第15次被覆盖了
这里第13次所对应的事务早已经提交了

然后oracle会按照原事务已提交做一系列的工作。

这节课讲了
事务槽的知识
同时把行级锁也讲了
同时事务的操作流程这块又给大家讲了

2017年8月26日
文字:韵筝

你可能感兴趣的:(事务槽数量,dump,ITL,快速提交,行级锁)