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信息被覆盖了,就会报快照过旧的错误。

一致性.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表空间在内。

事务

  1. 首先当一个开始时,需要在回滚段事务表上分配一个事务表(事务槽)。
  2. 在数据块头部获取一个ITL事务槽,该事务槽指向回滚段头的事务槽。
  3. 在修改数据之前,需要记录前镜像信息,这个信息以UNDO RECORD的形式存储在回滚段中,回滚段头事务槽指向该记录。
  4. 锁定修改行,修改行锁定位(1b-lock-byte)指向ITL事务槽。
  5. 记录redo日志。
  6. 修改数据。
  7. 提交日志,日志号为提交时的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日志之前,所以不会有什么问题。

参考:
【1】http://www.dbaxiaoyu.com/archives/1987
【2】https://blog.csdn.net/tianlesoftware/article/details/7346212
【3】http://www.cnblogs.com/polestar/archive/2013/03/11/2953716.html
【4】https://blog.csdn.net/kobejayandy/article/details/50885693
【5】https://blog.csdn.net/tianlesoftware/article/details/5251916
【6】https://blog.csdn.net/wanghui5767260/article/details/20715809
【7】https://blog.csdn.net/dba_waterbin/article/details/7823519
【8】https://blog.csdn.net/wanghai__/article/details/4730722

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容