oracle分同步事务和异步事务,oracle的事务隔离

事务隔离

ITL

ITL: Interested Transaction List,也叫事务槽,它位于BLOCK Header。格式如下:

Itl Xid Uba Flag Lck Scn/Fsc

0x01 0x0001.026.00000150 0x008003c0.0113.17 ---- 1 fsc 0x0000.00000000

0x02 0x0005.013.00000190 0x0080004b.013a.17 ---- 1 fsc 0x0001.00000000

Xid:事务id,在回滚段事务表中有一条记录和这个事务对应,分别是undo segment number+undo segment slot+undo segment sequence,也就是回滚段编号、回滚段事务槽号、和回滚段的序号(回滚段事务槽被覆盖的次数)

Uba:回滚段地址,该事务对应的回滚段地址

Flag:事务标志位。这个标志位就记录了这个事务的操作,各个标志的含义分别是:

——- = 事务是活动的,或者在块清除前提交事务

C—- = 事务已经提交并且清除了行锁定。

-B— = this undo record contains the undo for this ITL entry

—U- = 事务已经提交(SCN已经是最大值),但是锁定还没有清除(快速清除)。

—-T = 当块清除的SCN被记录时,该事务仍然是活动的,块上如果有已经提交的事务,

Lck:影响的记录数

Scn/Fsc:快速提交(Fast Commit Fsc)的SCN或者Commit SCN。其实就是事务号。

SCN

system change number (SCN)是一个非常重要的标记,Oracle使用它来标记数据库在过去时间内的状态和轨迹。Oracle使用SCN来保存所有变化的轨迹。SCN是一个逻辑时钟来记录数据库事件。

隔离

当发出一条sql语句时,ORACLE会记录下这个时刻(SCN),然后在buffer cache中查找需要的BLOCK,或者从磁盘上读。当别的会话修改了数据,或者正在修改数据,就会在相应的block上记录ITL,此时ORACLE发现ITL中记录的SCN(Scn/Fsc)大于SELECT时刻的SCN,或者ITL没有SCN(正在修改,未提交);那么ORACLE就会根据ITL中的Uba找到UNDO信息获得该block的前镜像,然后在buffer cache 中构造出CR(consistent read)块,此时ORALCE 也会检查构造出来的BLOCK中ITL记录的SCN(Scn/Fsc),如果SCN(Scn/Fsc)还大于select时刻的SCN,那么一直重复构造前镜像,然后ORACLE找到前镜像BLOCK中的ITL的SCN是否小于select的SCN,同时检查这个事物有没有提交或者回滚,如果没有,那么继续构造前镜像,直到找到需要的BLOCK,如果在构造前镜像的过程中所需的UNDO信息被覆盖了,就会报快照过旧的错误。

bc0590c7f783?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

一致性.png

行级锁

锁分共享锁和排他锁,查询使用共享锁,修改使用排他锁,所以事务对修改的数据需要加排他锁,例如行级锁。行级锁实现通过对数据记录设置itl的序号,来说明数据记录已经被这个itl设置。

----------------

Start dump data blocks tsn: 0 file#: 1 minblk 61186 maxblk 61186

buffer tsn: 0 rdba: 0x0040ef02 (1/61186)

scn: 0x0000.000d206f seq: 0x01 flg: 0x02 tail: 0x206f0601

frmt: 0x02 chkval: 0x0000 type: 0x06=trans data

----------------

Itl Xid Uba Flag Lck Scn/Fsc

0x01 0x0001.026.00000150 0x008003c0.0113.17 ---- 1 fsc 0x0000.00000000

0x02 0x0005.013.00000190 0x0080004b.013a.17 --U- 1 fsc 0x0001.000d206f

-----------------

block_row_dump:

tab 0, row 0, @0x1f6e

tl: 13 fb: --H-FL-- lb: 0x0 cc: 2

col 0: [ 2] c1 02

col 1: [ 6] 6f 72 61 63 6c 65

tab 0, row 1, @0x1f55

tl: 12 fb: --H-FL-- lb: 0x2 cc: 2 --对应itl 0x02

col 0: [ 2] c1 03

col 1: [ 5] 6d 79 73 71 6c

tab 0, row 2, @0x1f61

tl: 13 fb: --H-FL-- lb: 0x1 cc: 2

col 0: [ 2] c1 04

col 1: [ 6] 6f 72 61 63 6c 65

end_of_block_dump

ITL Cleanout和Delayed block cleanout 和快速提交

在事务提交(commit)前,会在数据块的头部记录下这个Cleanout SCN(Csc)号、Undo Block Address(Uba)和Transaction ID(Xid);并且在在对应Interested Transaction List(Itl)中设置锁标志,记录这个事务在这数据块中产生的锁的数目;同时在对应修改的数据记录上打上行级锁标志,并映射到对应的Itl去。当提交时,并不会一一清除掉所有锁标志,而是给对应的Itl打上相应标志,告诉后面访问该数据块的事务,相应的事务已经提交。这就叫做快速提交(Fast Commit)。

ITL Cleanout

一个新的事务过来时,它首先会选择一个itl槽,首先oracle采用C---状态的事务,如果没有C---状态的事务,oracle就会发生一次itl cleanout,检查所有的ITL相关的事务,如果确认事务已经提交了,就将之修改为C---状态了。

Delayed block cleanout

1.如果一个transaction修改的block超过db cache的10%.

2.当一个事务未提交时,其修改过的block就已经写到硬盘上去了。此时事务提交了,并不会修改数据块上的状态。

仅仅为修改状态来重写数据块代价比较高,所以采用延迟修改。

可以看出块有可能在修改过程中,事务还没有commit的时候被写进磁盘。

已经提交的事务可以不用修改itl,从事务表中可以找到相应事务的状态,所以最终不会对结果造成任何影响,ITL Cleanout的时候也是这样查询真实的事务状态。

事务表条目有限,ITL Delayed block cleanout 丢失了事务表中的信息怎么办?

TRN TBL::

index state cflags wrap# uel scn dba parent-xid nub stmt_num cmt

------------------------------------------------------------------------------------------------

0x00 9 0x00 0x5b1a5 0x001c 0x0000.07ecd2be 0x0080030b 0x0000.000.00000000 0x00000002 0x00000000 1398758386

0x01 9 0x00 0x5b170 0x0011 0x0000.07ecd32d 0x00800326 0x0000.000.00000000 0x00000001 0x00000000 1398758405

0x02 9 0x00 0x5b1a0 0x000c 0x0000.07ecd2d7 0x0080030b 0x0000.000.00000000 0x00000001 0x00000000 1398758403

0x03 9 0x00 0x5b1a8 0x0000 0x0000.07ecd2b7 0x0080030a 0x0000.000.00000000 0x00000001 0x00000000 1398758386

0x04 9 0x00 0x5b1ad 0x000d 0x0000.07ecd2e4 0x0080030c 0x0000.000.00000000 0x00000002 0x00000000 1398758404

0x05 9 0x00 0x5b1a3 0x0021 0x0000.07ecd25d 0x0080030a 0x0000.000.00000000 0x00000001 0x00000000 1398758346

0x06 10 0x80 0x5b1ab 0x0002 0x0000.07ecd29c 0x00800326 0x0000.000.00000000 0x00000001 0x00000000 0

0x07 9 0x00 0x5b1a3 0x0012 0x0000.07ecd2ee 0x00000000 0x0000.000.00000000 0x00000000 0x00000000 1398758404

0x06的State 10表示这个回滚段对应的事务是active状态。如果发现事务表中的条目被覆盖了,例如itl记载的序列为100,现在为120,则认为事务已经提交,没有提交的事务为active,oracle是不会覆盖的。

undo, redo

Undo日志记录某数据被修改前的值,可以用来在事务失败时进行rollback;Redo日志记录某数据块被修改后的值,可以用来恢复未写入data file的已成功事务更新的数据。但是数据库崩溃了之后从日志的什么位置进行恢复?就引出了检查点的概念。

redo日志应首先持久化在磁盘上,然后事务的操作结果才写入db buffer,(此时,内存中的数据和data file对应的数据不同,我们认为内存中的数据是脏数据),db buffer再选择合适的时机将数据持久化到data file中。这种顺序可以保证在需要故障恢复时恢复最后的修改操作。先持久化日志的策略叫做Write Ahead Log,即预写日志。

WAL写日志 -> 修改db buffer -> 写入data file。

redo是对应的数据文件,undo是数据文件的一部分,所以undo的修改是需要进redo日志的,这个策略解决很大问题

检查点

WAL导致写数据文件是"异步"的,写日志文件是"同步"的。

这就可能导致数据库实例崩溃时,内存中的DB_Buffer 中的修改过的数据,可能没有写入到数据块中。数据库在重新打开时,需要进行恢复,来恢复DB Buffer 中的数据状态,并确保已经提交的数据被写入到数据块中。检查点是这个过程中的重要机制,通过它来确定,恢复时哪些重做日志应该被扫描并应用于恢复。

要了解这个检查点,首先要知道checkpoint queue概念,检查点发生后,触发DBWn,CKPT获取发生检查点时对应的SCN,通知DBWn要写到这个SCN为止, DBWR写dirty buffer 是根据 buffer 在被首次 modify的时候的时间的顺序写出,也就是 buffer被modify 的时候会进入一个queue (checkpoint queue),DBWr 就根据queue从其中批量地写到数据文件。 由于这里有一个顺序的关系,所以 dbwr的写的进度就是可衡量的,写到哪个buffer的时候该buffer的首次变化时候的scn就是当前所有数据文件block的最新scn,但是由于无法适时的将dbwr的进度记录下来,所以oracle 选择了一些策略。 其中就包括ckpt进程的检查点和心跳。

恢复

当数据库突然崩溃,而还没有来得及将buffer cache里的脏数据块刷新到数据文件里,同时在实例崩溃时正在运行着的事务被突然中断,则事务为中间状态,也就是既没有提交也没有回滚。这时数据文件里的内容不能体现实例崩溃时的状态。这样关闭的数据库是不一致的。

下次启动实例时,Oracle会由SMON进程自动进行实例恢复。实例启动时,SMON进程会去检查控制文件中所记录的、每个在线的、可读写的数据文件的END SCN号。

roll forward (前滚)

SMON进程进行实例恢复时,会从控制文件中获得检查点位置。于是,SMON进程到联机日志文件中,找到该检查点位置,然后从该检查点位置开始往下,应用所有的重做条目,从而在buffer cache里又恢复了实例崩溃那个时间点的状态。这个过程叫做前滚,前滚完毕以后,buffer cache里既有崩溃时已经提交还没有写入数据文件的脏数据块,也还有事务被突然终止,而导致的既没有提交又没有回滚的事务所弄脏的数据块。

rollback(回滚)

前滚一旦完毕,SMON进程立即打开数据库。但是,这时的数据库中还含有那些中间状态的、既没有提交又没有回滚的脏块,这种脏块是不能存在于数据库中的,因为它们并没有被提交,必须被回滚。打开数据库以后,SMON进程会在后台进行回滚。

必须先前滚,在回滚

回滚段实际上也是以回滚表空间的形式存在的,既然是表空间,那么肯定就有对应的数据文件,同时在buffer cache 中就会存在映像块,这一点和其他表空间的数据文件相同。

当发生DML操作时,既要生成REDO(针对DML操作本身的REDO Entry)也要生成UNDO(用于回滚该DML操作,记录在UNDO表空间中),但是既然UNDO信息也是使用回滚表空间来存放的,那么该DML操作对应的UNDO信息(在BUFFER CACHE生成对应中的UNDO BLOCK)就会首先生成其对应的REDO信息(UNDO BLOCK's REDO Entry)并写入Log Buffer中。

这样做的原因是因为Buffer Cache中的有关UNDO表空间的块也可能因为数据库故障而丢失,为了保障在下一次启动时能够顺利进行回滚,首先就必须使用REDO日志来恢复UNDO段(实际上是先回复Buffer Cache中的脏数据块,然后由Checkpoint写入UNDO段中),在数据库OPEN以后再使用UNDO信息来进行回滚,达到一致性的目的。

生成完UNDO BLOCK's REDO Entry后才轮到该DML语句对应的REDO Entry,最后再修改Buffer Cache中的Block,该Block同时变为脏数据块。

实际上,简单点说REDO的作用就是记录所有的数据库更改,包括UNDO表空间在内。

事务

首先当一个开始时,需要在回滚段事务表上分配一个事务表(事务槽)。

在数据块头部获取一个ITL事务槽,该事务槽指向回滚段头的事务槽。

在修改数据之前,需要记录前镜像信息,这个信息以UNDO RECORD的形式存储在回滚段中,回滚段头事务槽指向该记录。

锁定修改行,修改行锁定位(1b-lock-byte)指向ITL事务槽。

记录redo日志。

修改数据。

提交日志,日志号为提交时的scn。

问题

UNDO日志没有持久化到磁盘,rollback怎么办?

生成UNDO日志时,UNDO信息是使用回滚表空间来存放的,相当于修改数据,会生成相应的REDO日志。所以恢复时需要先前滚后回滚,前滚可以恢复回滚表空间。

UNDO REDO datafile

redo--> undo-->datafile

insert一条记录时,表跟undo的信息都会放进 redo 中,在commit 或之前, redo 的信息会放进硬盘上。 故障时, redo 便可恢复那些已经commit 了的数据。

AWL, 任何修改操作需要先写日志成功, 但是orcale在提交时才将日志写磁盘,详情将下节。commit,提交仅仅是生成事务号,提交当前的scn,记录下提交标志,将REDO日志刷到磁盘。

一次事务严格要执行多少次刷盘 【猜测】

首先生成UNDO日志,相应的要生成REDO日志,如果有多条UNDO日志,最后只调用一次刷盘就行,因为此时还没修改数据。

一种策略:

1.批量写入UNDO的REDO日志。

2.刷盘

3.写UNDO日志

4.批量写入数据修改的REDO日志。

5.批量修改DBbuffer。

6.提交。

7.刷盘。

提交事务的时候,生成事务号,写提交标志,这个时候REDO需要刷盘,成功刷盘意味着事务成功提交。

一次事务基本需要2次刷盘。

另外一种:【mysql一次事务一次刷盘配置是这种】

用户只在提交事务时强制sync一次。为了避免数据修改一部分down机,undo数据的redo还没写盘问题。采取的解决还是日志先行策略,Btree中的数据写入磁盘前,数据块对应的redo要先写到磁盘中。

总之,发起事务方强制在提交事务时sync一次,数据库机制保证在数据块写磁盘时,强制相应redo先入盘。一般的情况是数据修改在内存中,undo的redo日志以及数据块的redo日志都在内存中,当数据库要将数据块写盘时,会先将对应的redo日志写盘,undo的redo日志在数据块的redo日志之前,所以不会有什么问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值