【深度好文】有关延迟块清除和一致性读

《ORA-1555错误解决一例》一文中,当时尝试模拟UNDO段头事务表被覆盖的情况下出现ORA-01555错误,没有成功。实际上没有成功的原因是事务数虽然多,但是事务过小,使UNDO块没有被覆盖完,这样通过回滚事务表仍然能够得到事务表以前的数据。本文进一步讨论一些有关延迟块清除和一致性读方面的内容.

先来看看一个数据块中ITL的转储:

我们看看ITL中的第2个条目,其Flag为"C-U-",C表示commit(提交),U表示Upper bound(上限),这里”U“表明ITL中记录的事务的提交SCN并不是精确的,只是表明事务提交的精确SCN是在ITL中记录的SCN之前,对于第2条ITL来说,其事务提交SCN小于或等于”0x0b08.12fb5cae“。

那么这里的问题是:Upper bound是在什么情况下出现的?如果一个SQL语句对该块进行一致性读时,发现ITL中的Upper bound的SCN比一致性读需要的SCN大,这时会发生什么?

要回答这些问题,先来看下面的一系列测试过程:

1. 在会话1中建测试表t1,将插入500行数据,每个块只有1行数据,一共500个块,然后再创建一个较大的测试表t2,插入1000行数据:

表已创建。

2. 在会话1中更新测试表T1中的所有行,并获取事务ID,然后再dump1个数据块和事务对应的UNDO段头块:

事务使用的事务表在回滚段_SYSSMU7$上,即第7个回滚段。事务表中的条目为21,即事务表中的第21条记录。

数据块dump出来的结果是(去掉了对本文话题无关紧要的内容,以后也是如此):

可以看到ITL中的第2条正是当前活动事务在这个块上所使用的ITL。

Xid为“0x0007.015.00004ba0”,转换成10进制正是“7.21.19360”,这跟之前查询出来的事务ID是一致的。ITL中此时的flag为"----",正是活动事务的标志。由于块中只有1行数据,因此Lck为1,即该事务在这个块中锁住的行数为1行。

下面再来看看此时UNDO段头块的转储结果:

可以看到index为0x15(即10进制21)的行,其wrap#为0x4ba0,与事务的xid相符,同时其state为10,表示事务是活动的,而事务表项上的scn值也是跟之前从v$transaction中查询出来的值是完全一致的。

3. 在会话1中将所有的block刷出内存,然后提交,这样T1表中的所有块上的事务都不会被清除,再将UNDO段头dump出来:

看看UNDO段头的转储结果:

从上面的内容可以看到,state已经变为9,表示事务已经提交,提交SCN为0x0b08.12f4649c,而UNDO段头中的“TRN CTL”中的“ctl: 0x0015”表示这一条事务表记录将会是该事务表中SCN最大的一条,即会最后被选择重用。

4. 现在我们在会话2中,使用只读事务来模拟开始长时间的查询,其查询时间点就是只读事务设置时当前的时间点:

5. 然后在会话3里面,发起960次事务,但是每个事务都很少:

由于我测试的数据库,UNDO表空间中的UNDO段有10个(同时全部都是处于online状态),每个UNDO段头上的事务表有48条记录(在10g中8K大小的块其固定为48条,随着版本和块大小的不同,事务表的大小有变化)。所以960次事务,事务表中的每条记录平均被重用2次,而由于事务较小,UNDO表空间足够大,那么UNDO块不会被覆盖完,即之前会话1上对T1表所做的事务产生的UNDO是不会被覆盖的。

6. 在会话1中,然后转储之前T1表的事务使用的UNDO段头块,得到的内容如下:

可以看到,这里seq从原来的0x16bf变成了0x1708,即事务表中的记录用了73次,看来在各个UNDO段头之间的事务表的使用并不完全是平均的。

7. 在会话1中,查询T1表中的1条记录,然后再转储T1表上第1行数据的数据块:

由于T1表之前的事务在块上的信息并没有清除,因此毫无疑问应该会进行延迟块清除,从上面ITL的数据来看,第2条,也就是之前的事务所使用的那条ITL,Flag已经是"C-U-“,同时Lck为0,表示事务已提交。那么事务提交的SCN是多少呢,这里的SCN是0x0b08.12fb5cae,由于Flag中有"U",表示这个SCN只是一个上限值,这里记录的SCN并不是精确的事务提交SCN,只是表明事务的SCN会比小于等于现在记录的这个SCN值。

那么这个值是怎么来的?这正是之前提出的其中一个问题。

可以确定的是,这个块的延迟清除是由会话1发起的,现在描述一下这个过程:会话1查询T1表,很显然这个时候T1表的事务早已经提交,所以我们知道它不是需要回滚表块上的数据的;但是程序就是程序,它只能按照逻辑来进行操作,它检查块的ITL列表时,发现有一个活动事务(由于之前的事务没有清除),所以它通过ITL记录的xid去检查事务是否已经提交,它从UNDO段头事务表的第0x15条记录发现,该记录的wrap#是0x4ba2,并不是事务的seq号0x4ba0,因此这条记录已经被重用,那么可以确认这条记录已经提交,需要进行事务延迟块清除;那么事务的提交SCN是多少呢?

由于事记录表中的记录已经被重用,所以不能直接得到;接下来会话会从UNDO段头的"TRN CTL::"部分得到一个SCN值:0x0b08.12fb5cae,这个SCN是事务表中最近被重用的事务表记录之前的事务的提交SCN,由于事务表记录重用是按照提交SCN从小到大的顺序重用的,所以可以说这个SCN比事务表中目前所有记录的事务的提交SCN都要小,但它又是这个事务表中曾经被重用过的事务记录的提交SCN中的最大值,由于我们要查看的事务记录已经被重用过,所以很显然该事务的提交SCN会小于或等于"TRN CTL"记录的SCN。

基于这个原理,会话1知道,ITL中需要清除的事务小于等于UNDO段头中TRN CTL记录的SCN,而这个SCN值已经比它的一致性读SCN要小,所以会话1足够判断不需要回滚该事务就能得到一致性读结果,所以这个时候,它就直接将TRN CTL中的SCN作为ITL中的事务提交SCN的上限值,并将Flag设置为"C-U-"。这里会话1不需要回滚事务表就已经足够,它不会做多余的工作来得到精确的事务提交SCN,只需要能判断事务提交时间小于一致性读的SCN就可以了。

8. 在会话2中,执行下面的SQL:

SQL> select * from t1 where rownum<=1;

由于会话2在会话3的大量事务之前就设置了一致性读的起点(通过设置事务为只读模式),很显然,这个时间点比上述第7个操作步骤中会话1的查询时间要早得多,也在上面提到的上限SCN 0x0b08.12fb5cae要小。那么此时会发生什么?

9.在会话1中再一次转储T1表第1行数据所在的数据块,得到的内容如下:

从上面的结果可以看到,SCN也变成了 0x0b08.12f4649c,这正是T1表上之前的事务提交的准确的SCN,事务Flag从原来的”C-U-“变成了“C---”也说明了现在的SCN是精确的事务提交SCN。这说明了什么?这说明当会话2在读到这个块时,虽然发现事务已经提交,但是其记录的SCN是个上限值,并且大于其一致性读时间点,所以它还需要进一步来查找该事务更接近精确的提交SCN。这个时候就会要回滚事务表,

实际上,由于在T1表的事务提交之后而在会话2的查询时间点之前这段时间内,在这个UNDO段中没有事务产生,所以会话2在回滚事务表到最后,就刚好发现了事务的精确提交SCN。那么,这里得到一个重要结论是:如果会话发现ITL中的事务有比现在记录的上限SCN有更精确或更小的提交SCN时,会用新找到的SCN替换到ITL中现在记录的SCN。也同样是一个修改块的操作,也可以认为是一次“块清除”。下面的统计数据是会话2执行SELECT语句的统计数据:

immediate (CR) block cleanout applications值为1表示有1次逻辑读产生的块清除,还有"transaction tables consistent reads - undo records application"表示产生了事务表的回滚。

接下来,我们再做一个测试,具体的测试数据不再列出,测试表仍然是T1和T2,步骤如下:

  1. 会话1更新表T1,将buffer cache中的块全部刷出内存,然后再提交。
  2. 会话1发起960次小事务更新表T2。
  3. 会话2执行set transaction read only。
  4. 会话1发起960次小事务更新表T2。
  5. 会话3执行set transaction read only。
  6. 会话1发起960次小事务更新表T2。
  7. 会话2查询表T1。
  8. 会话3查询表T2。

上面步骤的第7步完成后,数据块的ITL如下:

第8步完成后,数据块的ITL如下:

上面两次列出的ITL中的第2个条目是我们要关注的,可以看到,会话2查询T1表之后,Flag为”C-U-“,记录的SCN为0x0b08.1326501e,会话3查询T1表之后,Flag仍然为”C-U-“,SCN发生了变化,为0x0b08.1324d090,这个SCN值更小(实际上第1个ITL条目也在变化),但仍然不是精确的提交SCN。

上面的测试这也证明了本文得到的两个结论:

  1. 在一致性读时进行块清除,并不需要得到事务的精确提交SCN(尽管它通过回滚事务表可能会得到精确的提交SCN),只需要它发现事务的提交SCN比自己的一致性读的时间点小就可以了。
  2. 如果在一致性读时发现了更小的(更接近精确提交的)SCN值或精确的提交SCN值,它还会做一次块清除,修改ITL,以记录更小或更精确的SCN。

原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2017-05-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

打狗棒法之:Cknife(C刀)自定义模式秒过安全狗

免责声明:本站提供安全工具、程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负! 0x00 前言 很多朋友在使用Cknife时都误以为她只是菜刀的跨平...

2328
来自专栏纯洁的微笑

分布式调用链监控组件的实践与比较(一)实践

引言:最近在调研与选型分布式调用链监控组件。选了主要的三种APM组件进行了实践与比较。本来打算一篇文章写完的,篇幅太长,打算分两篇。本文主要讲下链路tracei...

1368
来自专栏北京马哥教育

一秒内诊断 Linux 服务器的性能

60,000 毫秒内对 Linux 的性能诊断 当你为了解决一个性能问题登录到一台 Linux 服务器:在第一分钟你应该检查些什么? 在 Netflix,我们有...

2646
来自专栏FreeBuf

关于Shell你想知道的都在这儿

0x01 前奏说明 很多时候,不到万不得已的情况下,我们完全没必要非往目标机器里传一堆工具,先不说由于各种防护[不仅仅是杀软的问题],传工具困难重重,有时由于自...

2815
来自专栏七夜安全博客

(原创)Scrapy爬取美女图片

1975
来自专栏aoho求索

几种分布式调用链监控组件的实践与比较(一)实践

引言:最近在调研与选型分布式调用链监控组件。选了主要的三种APM组件进行了实践与比较。本来打算一篇文章写完的,篇幅太长,所以分了两篇。本文主要讲下链路trace...

4158
来自专栏Samego开发资源

让子弹飞~利用OPcache扩展提升PHP7性能 | laravel篇

What is OPcache OPcache是PHP中的Zend扩展,可以大大提升PHP的性能。 OPcache 通过将 PHP 脚本预编译的字节码存储到...

832
来自专栏linux运维学习

linux学习第二十七篇:使用w查看系统负载,vmstat,top,sar,nload命令

使用w查看系统负载 w/uptime的命令结果是一样的 ? 1. 第一行从左面开始显示的信息依次为:时间,系统运行时间,登录用户数,平均负载。 ...

1847
来自专栏程序员八阿哥

年薪20万Python工程师进阶(7):Python资源大全,让你相见恨晚的Python库

用来访问第三方 API的库。 参见: List of Python API Wrappers and Libraries。

1402
来自专栏Android 开发者

Android 8.1 开发者预览版 —— 为 ML 机器学习做好准备的 Oreo 升级版

1894

扫码关注云+社区