oracle是如何创建cr块构造一致性读的
Oracle如何创建cr块构造一致性读的 ?
01
1 Buffer cache工作原理
在没有解释这个问题之前,我们先来明确部分buffer cache工作原理知识,以此帮助大家更好的去理解问题本质,其实这篇文章本不应该写,周六大家群内讨论xcur scur cr期间有了些不同的看法,整体思路都是正确的。中间我会稍微磨叽一些基础的工作原理和概念性知识,通过这些概念帮助我们更深入的理解问题本质。
1.1
1.1buffer cache的概念
buffer cache我们最经常称呼的名称其实就是缓冲区高速缓存,用于存储从数据文件读取的block副本的内存区域而已,所谓缓冲区其实就是缓冲区管理器用来暂时缓存当前的,或者最近使用的block主内存地址,所有同时连接一个数据库实例的用户,都会依靠共享的模式访问buffe cache.
1.2
1.2 buffer cache内存区域的目的
第一个目的一定是优化物理i/o了,简单的说,就是block更新在缓存中的block,然后把更改后的元数据存储在重做日志缓冲区,commit操作以后,oracle将重做缓冲区写入磁盘,但记住不一定会立即写入,相反dbwn进程会在后台执行惰性写入操作,当我们开始把频繁访问的block放到buffer cache,然后将那些不经常存取的block写到磁盘去,这是一种良性设计方案在我看来。
1.3
1.3 buffer cache的状态标记
1.3.1unused的
简单的说就是buffer cache可以被使用,因为它从未使用过,或者说当前未使用,这种类型的缓冲区其实是oracle最容易使用的
1.3.2clean的
也就是说这个buffer之前被使用过,而现在包括某个block在某个时间点的读取一致版本,块包含数据但是干净的,所以压根就不需要执行什么检查点操作,oracle完全可以重用它。
1.3.3dirty的
那些个已经被修改的buffer,但是还没有写到磁盘进行持久化的block就是脏的,oracle总是要在重用该block之前必须要执行检查点操作。
注意:
其实从概念上讲,只有一个lru,但为了并发,oracle实际上使用了很多个lru。
1.4
1.4buffer cache模式
这是我们今天要讨论的主题。
1.4.1
当客户端程序开始请求数据时候,oracle提供两种模式之一从buffer cache检索数据对应的buffer.
1.4.1.1 current mode
当前模式,其实我更愿意叫数据块获取,这是一种对当前已经出现在buffer cache中的block检索,比如说,一个未提交的事务已更新某个快中的两行,则当前模式获取会检索这个具有未提交行的块,oracle最经常使用数据块获取的情况是修改语句期间,它只需要更新块的当前版本
1.4.1.2 老生常谈的consistent mode
一致性获取其实很简单,无非就是对于某个block的一致性读取版本的检索此检索可能会使用撤销数据,比如说,如果一个未提交的事务已更新某个块中的若干行,而在另一个独立会话中查询操作开始请求这个block,Oracle就会使用撤销数据来创建该块的读一致性版本,我们也可以叫一致性读取克隆标准本质上它并不包括未提交的更新,一般情况下,查询都是以一致性模式检索数据块的。
1.5
1.5 我们举个简单的例子:
1.5.1
刚才我们谈到了,通常来讲,查询是通过oracle的读一致性来检索数据的,OK,那么这种机制利用的其实就是撤销数据来显示以前版本的数据,目的其实只有一个:就是为了保证查询所读取的所有block都在单点时间是一致的。
1.5.2
假设哈,一个查询在一次table access full全表扫描中必须要读取100个block,当该查询处理前面10个block时候,而另一个dml操作修改了这笔查询需要的某个数据块,假设是第75个block,OK,这个时候当第一个查询读取到第75个block的时候,发现数据已经被修改了哈,这个时候就会使用撤销数据去检索旧的,没有经过修改的版本,然后oracle要干一个事,在内存里去构造一个第75数据块的非当前版本。
1.5.3
这个时候,必须修改数据的dml操作,使用读取一致性的机制来检索,只与修改开始时的搜索条件匹配的数据,然后呢,这些语句开始检索数据块,如同他们处于当前状态其实是一样的,当做出必要的修改后,oracle必须要去执行其他和数据修改有关系的操作,比如说生成重做日志和撤销数据。
----------------------------------------------------------------
02
2 oracle是怎么做到创建cr一致性的
当我们尝试去理解这些工作原理和概念以后,来回归今天的主题,oracle是怎么做到创建cr一致性的?
2.1
2.1 举个例子
举个例子:Oracle 数据库始终强制执行语句级读取一致性, 保证单个查询所返回的数据是已提交的、 且关于某个单一时间点一致。单个 SQL 语句所一致的时间点取决于事务的隔离级别和查询的性质。
这个所谓的单点时间其实就是scn,在读提交隔离级别,该时间点是语句打开的时间。例如, 如果一个SELECT 语句在 SCN 1000 时打
开,则此语句一致于 SCN 1000。
在可串行化或只读事务隔离级别, 该时间点为事务开始的时间。例如, 如果一个事务开始于 SCN 1000, 且在该事务中有多个SELECT语句发生, 则每个语句都一致于 SCN 1000.
当我们查询数据时,将显示查询时间/SCN#时的数据。如果所需的块不在缓冲区中,从磁盘读取块(状态=xcur),如果块的克隆已经在缓冲区中,选择的克隆时间刚好晚于所需的时间。将Undo应用于克隆,使其与查询的时间/SCN#保持一致(status=cr)。
2.2
2.2 xcur cr是什么?
注意:xcur cr是什么?其实就是buffer的状态
oracle官方给出了如下几种状态的解释说明:


注意:
如果块的克隆已经在缓冲区中,选择的克隆时间刚好晚于所需的时间。将Undo应用于克隆,使其与查询的时间/SCN#保持一致(status=cr)。
如果块在缓冲区中,并且是脏的,并且没有克隆,复制(克隆)当前块。将撤销应用于克隆,以使其与查询的时间/SCN#一致(状态=cr)
DML总是在块的当前副本上执行(Status=xcur)。
每当对块进行更新时,如果当前块在缓冲区中(status = xcur),对当前块进行克隆(status=cr),对当前块执行更新(状态=xcur) 否则(所需的块不在缓冲区中),从磁盘读取 块(状态=xcur),块的克隆也进行了(status=cr),对当前块执行更新(状态=xcur)
当对同一个块发出多个更新/查询时,可能会创建同一块的多个CR块。由于同一块的所有CR克隆都在同一条链上,因此搜索该块的进程将花费很长时间扫描链,并且会出现CBC锁存的争用,简单说就是latch:cache buffer chain list 。因此Oracle限制了最大no。将一个块克隆到(一个current(xcur)和5个CR克隆)。但这是一个软限制,因为如果同一块中有超过5个进程同时更新不同的行,那么可能会创建超过6个CR块。
03
3.支撑工作原理和理论知识最好的方法就是实践
3.1
3.1 初始化试验脚本:






注意:
结果集对应12号数据文件第414号数据块,同时对应object_id为98820
3.2
3.2 检查x$bh
我们有一个当前块(状态=xcur)。当前块总是一个且只有一个。

具体脚本如下:

----------------------------------------------------------------
3.3
3.3 DML如何生成CR块
现在,让我们看看DML如何生成CR块
注意:第一次尝试更新:
UPDATE T_600_DEMO set COL2 = '如人饮水冷暖自知分享CR一致性 查询知识',COL3 = '如人饮水冷暖自知分享CR一致性查询知识',COL4 = '如人饮水冷暖自知分享CR一致性查询知识'
WHERE COL1 = 1

我再一次尝试更新,由于上一笔操作未提交,这笔操作会被hang住,还是同样的update语句
UPDATE T_600_DEMO set COL2 = '如人饮水冷暖自知分享CR一致性查询知识',COL3 = '如人饮水冷暖自知分享CR一致性查询知识',COL4 = '如人饮水冷暖自知分享CR一致性查询知识' WHERE COL1 = 1

注意:以下不在截图,对于cr状态的克隆模板我们要做超过5个克隆模板出来:

注意:
已经创建了另一个cr克隆。每个克隆在创建时都有一个不同的SCN#,这是需要注意的2.看到没,当我们尝试update 7次,cr的状态却只保留个副本,新的cr克隆还没有创建,因为已经达到了6 (1 xcur和5 cr)的极限。
我们锁定一下_db_block_max_cr_dba控制副本的个数和简单说明。

脚本如下:

注意:
这里的DBA可不是大家的职位名称,DBA指的是每个data block address允许的最大cr缓冲区的数量。2.OK,到这里谜底解开,我们明确了cr一致性副本并不是无限制的创建,通过_db_block_max_cr_dba参数去控制
好的,有的细节知识需要说清楚:
04
4 有的细节知识需要说清楚
4.1
4.1 X$BH 视图STATE字段代表Buffer的状态
以下是状态列表

4.2
4.2 案例继续
接着上面的案例,当我们在rac另一个节点尝试去做操作的时候:
--实例2开始操作:
SQL> UPDATE T_600_DEMO set COL2 = '如人饮水冷暖自知分享CR一致性查询知识',COL3 = '如人饮水冷暖自知分享CR一致性查询知识',COL4 = '如人饮水冷暖自知分享CR一致性查询知识' WHERE COL1 = 1
1 row updated.
--实例2新开一个session
SQL> select state,cr_scn_bas from x$bh where file#=12 and dbablk=414 and state!=0; --注意,不要写错了,还是12号数据文件414号数据块
STATE CR_SCN_BAS
---------- ----------
1 0
3 1227641
3 1227638
-------------------------------------------------------------
注意:
这个演示中我们需要用到的是 : state =1 Xcurrent 、 state=2 Scurrent 、 state=3 CR2.实例2更新 同一个数据块内的另一条记录 ,这回引发 gc current block 2 way 并将Current Block 传输到实例2,
同时 实例1的原Current Block Convert 成 Past Image STATE =1 的Xcurrent block已传输到实例2 , 再来看 实例1此时的 GC状态。
SQL> select state,cr_scn_bas from x$bh where file#=12 and dbablk=414 and state!=0;
STATE CR_SCN_BAS
---------- ----------
3 1227641
3 1227638
8 0
3 1227595
------------------------------------------------------------
以下为执行SELECT查询前设置了event 10708和部分trace信息:


注意:
通过TRACE不难发现 因为之前没有收集过t_600_demo表的统计信息, 所以这里出发了Dynamic Sampling的动态采样,这本身会引发对t_600_demo表的 CR读请求,实际产生了一次gc cr block 2-way等待。
2018-09-09 15:28:33.632654 : kjbsentscn[0x0.12bbc1][to 2]12bbc1= 1227713 与上述X$BH中的一个CR块对应,kjbsentscn[0x0.12bbc1][to 2] 可以理解为向实例2发送了SCN=12bbc1=1227713 DBA=0x15c91.1 98820.0 的 CR Request(obj#=98820)
之后kjbrcvdscn函数确认了 [no bscn <= rscn 0x0.12bbc1][from 2] ,即没有 比已receive的 SCN Version =12bbc1 更好的Best Version
----------------------------------------------------------------
05
5. 再来看老哥如下演示:
5.1
5.1 隐含参数设置
设置隐含参数_enable_minscn_cr=false 和 _db_block_max_cr_dba=20 并重启RAC所有实例

注意:在实例2中update更新一次数据块 就对应地在实例1中查询一次,这就是反复的在实例1中请求cr块的过程
5.2

下面为实例1的 X$BH记录


注意:
如上述演示在设置了
_enable_minscn_cr(enable/disable minscn optimization for CR)=false
和 _db_block_max_cr_dba=20 (Maximum Allowed Number of CR buffers per dba) 2个 参数后 最多的时候实例1中缓存了同一个数据块的 多达 19个版本的CR
_enable_minscn_cr是11g以后出现的新隐藏参数,它控制Oracle是否计算CR块的最小SCN,当Foreground Process Receive接收到同一个数据块的更新(SCN更大)的SCN Version CR Block时可能会清除CBC上的 SCN较小的、旧的CR块 , 这样做的目的是减少Buffer Cache中同一个数据块不同版本SCN Version的CR块的数量。
注意不管是语句级别或者事务级别 其所要求的Snap_Scn 快照 SCN总是 语句或事务开始时的Current SCN, 保留一些旧的CR块虽然可能对一些持续时间长的查询或者游标有益,但是实例Buffer Cache中 同一个数据块的多版本 CR块的总数量是有限的, 这个总数受到 _db_block_max_cr_dba隐藏参数的控制。
如我们上述演示中设置为20 ,则最多可以在Buffer Cache中缓存多大19个版本的CR块; 注意该_db_block_max_cr_dba参数的默认值为6 , 即一个实例Buffer cache中同一个数据块的CR 版本同时不多于6个。
ora-600 QQ群 851604218
单枪匹马你别怕,一腔孤勇又如何,这一路上你可以哭,可以走弯路,但不能怂,总得熬过无人问津的日子。
---致敬所有在路上的Oracler们




