暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

Oracle原理探讨--Oracle如何构造一致性读

Oracle蓝莲花 2021-04-15
1525

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官方给出了如下几种状态的解释说明:



注意:

  1.  如果块的克隆已经在缓冲区中,选择的克隆时间刚好晚于所需的时间。将Undo应用于克隆,使其与查询的时间/SCN#保持一致(status=cr)。




  2. 如果块在缓冲区中,并且是脏的,并且没有克隆,复制(克隆)当前块。将撤销应用于克隆,以使其与查询的时间/SCN#一致(状态=cr)




  3. DML总是在块的当前副本上执行(Status=xcur)。




  4. 每当对块进行更新时,如果当前块在缓冲区中(status = xcur),对当前块进行克隆(status=cr),对当前块执行更新(状态=xcur)       否则(所需的块不在缓冲区中),从磁盘读取     块(状态=xcur),块的克隆也进行了(status=cr),对当前块执行更新(状态=xcur)




  5. 当对同一个块发出多个更新/查询时,可能会创建同一块的多个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个克隆模板出来:

注意:

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

  2. 我们锁定一下_db_block_max_cr_dba控制副本的个数和简单说明。


脚本如下:


注意:

  1.  这里的DBA可不是大家的职位名称,DBA指的是每个data block address允许的最大cr缓冲区的数量。2.OK,到这里谜底解开,我们明确了cr一致性副本并不是无限制的创建,通过_db_block_max_cr_dba参数去控制

  2. 好的,有的细节知识需要说清楚:

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

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

注意:

  1.  这个演示中我们需要用到的是 : state =1 Xcurrent 、 state=2 Scurrent 、 state=3 CR2.实例2更新 同一个数据块内的另一条记录 ,这回引发 gc current block 2 way 并将Current Block 传输到实例2,

  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信息:




注意:

  1.  通过TRACE不难发现 因为之前没有收集过t_600_demo表的统计信息, 所以这里出发了Dynamic Sampling的动态采样,这本身会引发对t_600_demo表的 CR读请求,实际产生了一次gc cr block 2-way等待。

  2.  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)

  3. 之后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记录


注意:

  1.  如上述演示在设置了

     _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

  2. _enable_minscn_cr是11g以后出现的新隐藏参数,它控制Oracle是否计算CR块的最小SCN,当Foreground Process Receive接收到同一个数据块的更新(SCN更大)的SCN Version CR Block时可能会清除CBC上的 SCN较小的、旧的CR块 , 这样做的目的是减少Buffer Cache中同一个数据块不同版本SCN Version的CR块的数量。

  3. 注意不管是语句级别或者事务级别 其所要求的Snap_Scn 快照 SCN总是 语句或事务开始时的Current SCN, 保留一些旧的CR块虽然可能对一些持续时间长的查询或者游标有益,但是实例Buffer Cache中 同一个数据块的多版本 CR块的总数量是有限的, 这个总数受到 _db_block_max_cr_dba隐藏参数的控制。

  4. 我们上述演示中设置为20 ,则最多可以在Buffer Cache中缓存多大19个版本的CR块; 注意该_db_block_max_cr_dba参数的默认值为6 , 即一个实例Buffer cache中同一个数据块的CR 版本同时不多于6个。


ora-600    QQ群 851604218

      单枪匹马你别怕,一腔孤勇又如何,这一路上你可以哭,可以走弯路,但不能怂,总得熬过无人问津的日子。

                ---致敬所有在路上的Oracler们


文章转载自Oracle蓝莲花,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论