shared pool 和buffer pool 详解(之二, Cache Buffers LRU Chain、Cache Buffers LRU Chain闩锁竞争与解决)

【深入解析--eygle】学习笔记

 

1.1.2  Cache BuffersLRU Chain闩锁竞争与解决

 

当用户进程需要读数据到Buffer Cache时或Cache Buffer根据LRU算法进行管理等,就不可避免的要扫描LRU  List获取可用Buffer或更改Buffer状态,我们知道,Oracle的Buffer Cache是共享内存,可以为众多并发进程并发访问,所以在搜索的过程中必须获取Latch(Latch是Oracle的一种串行锁机制,用于保护共享内存结构),锁定内存结构,防止并发访问损坏内存中的数据(我们必须认识到对于数据的访问、Buffer的存取就意味着多次的Latch访问,而过于严重的Latch竞争常常是系统的瓶颈所在)。

这个用于锁定LRU的Latch就是我们经常见到的Cache Buffers Lru Chain。

 

select addr, latch#,name, gets, misses, immediate_gets, immediate_misses

  from v$latch

 where name = 'cache buffers lru chain';

 

16:48:46 sys@felixSQL>select addr, latch#, name, gets, misses, immediate_gets,immediate_misses

17:19:05   2   from v$latch

17:19:05   3  where name = 'cache buffers lru chain';

 

ADDR                 LATCH# NAME                        GETS     MISSES IMMEDIATE_GETS IMMEDIATE_MISSES

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

0000000060019F08        150 cache buffers lru chain    29129          2          25156                0

 

17:19:06 sys@felix SQL>

 

可以从v$latch_children视图察看当前各子Latch使用情况:

 

select addr,

      child#,

       name,

       gets,

      misses,

      immediate_gets   igets,

       immediate_missesimisses

  fromv$latch_children

 where name= 'cache buffers lru chain';

 

17:19:06 sys@felix SQL>select addr,

17:46:11  2         child#,

17:46:11  3         name,

17:46:11  4         gets,

17:46:11  5         misses,

17:46:11  6         immediate_gets   igets,

17:46:11  7         immediate_misses imisses

17:46:11  8    from v$latch_children

17:46:11  9   where name = 'cache bufferslru chain';

 

ADDR             CHILD# NAME                     GETS     MISSES      IGETS   IMISSES

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

0000000077572578      16 cache buffers lru chain      0         0          0          0

00000000775724B0      15 cache buffers lru chain     16         0          1          0

0000000077556FE0      14 cache buffers lru chain      0         0          0          0

0000000077556F18      13 cache buffers lru chain     16         0          1          0

000000007753BA48      12 cache buffers lru chain      0         0          0          0

000000007753B980      11 cache buffers lru chain     16         0          1          0

00000000775204B0      10 cache buffers lru chain      0         0          0          0

00000000775203E8       9 cache buffers lru chain     16         0          1          0

0000000077504F18       8 cache buffers lru chain      0         0          0          0

0000000077504E50       7 cache buffers lru chain     16         0          1          0

00000000774E9980       6 cache buffers lru chain      0         0          0          0

00000000774E98B8       5 cache buffers lru chain  29417         2      25624          0

00000000774CE3E8       4 cache buffers lru chain      0         0          0          0

00000000774CE320       3 cache buffers lru chain     16         0          1          0

00000000774B2E50       2 cache buffers lru chain      0         0          0          0

00000000774B2D88       1 cache buffers lru chain     16         0          1          0

                                                 

16 rows selected.                                   

 

17:46:13 sys@felix SQL>

 

如果该Latch竞争激烈,通常有如下方法可以采用:

 

(1)  适当增大BufferCache,这样可以减少读数据到Buffer Cache的机会,减少扫描Lru List的竞争。

(2)  可以适当增加LRULatch的数量,修改_db_block_lru_latches参数可以实现,但是该参数通常来说是足够的,除非在Oracle Support的建议下或确知该参数将带来的影响,否则不推荐修改。

(3)  通过多缓冲池技术,可以减少不希望的数据老化和全表扫描等操作对于Default池的冲击,从而可以减少竞争。

(4)  优化SQL,减少数据读取,从而减少对于LRU List的扫描。

 

2.1.3 Cache Buffer Chain闩锁竞争与解决

在LRU和Dirty List这两个内存结构之外,Buffer Cache的管理还存在另外两个重要的数据结构:Hash Bucket和Cache Buffer Chain。

 

1.Hash Bucket和Cache Buffer Chain

我们可以想象,如果所有的Buffer Cache中的所有Buffer都通过同一个结构管理,当需要确定某个Block在Buffer中是否存在时,将需要遍历整个结构,性能会相当低下。

为了提高效率,Oracle引入了Bucket的数据结构,Oracle把管理的所有的Buffer通过一个内部的Hash算法运算后存放到不同Hash Bucket中,这样通过Hash Bucket进行分割之后,众多的Buffer被分布到一定数量的Bucket之中,当用户需要在Buffer中定位数据是否存在时,只需要通过同样的算法获得Hash值,然后到相应的Bucket中查找少量的Buffer即可确定。

 

每个Buffer的存放的Bucket由Buffer的数据块地址(DBA,Data Block Address)运算决定。在Bucket内部,通过Cache Buffer Chain(它是一个双向链表)将所有的Buffer通过BufferHeader信息联系起来。

Buffer Header存放的是对应数据块的概要信息,包括数据块的文件号、块地址、状态等。在判断数据块在Buffer中是否存在,通过检查Buffer header即可确定

 

让我们通过一个现实的场景来回顾一下这个过程。

如果大家去过老一点的图书馆,查找过手工索引,你可能记得这样的场景:树立在你面前的是一排柜子(那是相当的壮观),柜子又被分为很多小的抽屉,抽屉上按照不同的分类方法标注了相关信息,比如按开头字母顺序,如果我们要查询Oracle相关书籍,就需要找到标记有“O”的 抽 屉 。打 开 抽 屉 ,我们 会 看 到 一 系 列 的 卡 片 ,这 些 卡 片 通 常 被 一 根铁闩串起来(通常就是一个铁丝),每根卡片上会记录相关书籍的信息,可能包括书籍名称、作者、ISBN号、出版日期等,当然这些卡片上还存储了一个重要的信息,就是书籍存放的书架位置信息,有了这个信息,通过翻阅这些卡片,就可以快速地找到想要的书籍,并且 在 需 要 时 能 够 快 速 从 图 书

 

馆浩如烟海的图书中找到我们需要的一本。

 

在这里,图书馆就是我们的Buffer Cache,这个Cache可能因为“图书数量”的增加而不断扩大;每个抽屉都是一个Bucket,这个Bucket中存放了根据一定的分类方式(也就是通过Hash运算)归入的图书信息,也就是Buffer Header;抽屉中的每张卡片就是一个Buffer Header,这些Buffer  Header上记录了关于数据块的重要信息,如DBA等;这些卡片在Bucket中,通过一个铁闩串接起来,这就是Cache Buffer Chain

 

由于每个抽屉只有一根铁闩,如果很多同学都想翻阅这个链上的卡片,那么就产生了Cache  Buffer  Chain的竞争,先来到那个同学持有了Latch就能不停的翻阅,其他同学只好不停的来检查,当然如果检查次数多了(超过了_spin_count), 也 可 以 去 休 息 室 小 憩 一 会 , 再来和其他同学争夺。

从Oracle 9i开始,对于Cache Buffer Chain的只读访问,其Latch可以被共享。也就是说,如果大家都只是翻一翻卡片,那么大家可以一起读,但是如果有人要借走这本书,那么就只能独享这个Latch了。这就是Buffer Cache与Latch竞争。

 

由于Buffer根据Buffer  Header进行散列,最终决定存入那一个Hash  Bucket,那么Hash Bucket的数量在一定程度上决定了每个Bucket中Buffer数量的多少,也就间接影响了搜索的性能。所以在不同版本中,Oracle一直在修改算法,优化Hash Bucket的数量。我们可以想象, Bucket的数量多一些,那么在同一时间就可以有更多的同学可以拿到不同的抽屉,进行数据访问;但是更多的抽屉,显然需要更多的存放空间,更多的管理成本,所以优化在什么时候都不是简单的一元方程。

Hash Bucket的设置受一个隐含参数_ DB_BLOCK_HASH_BUCKETS的影响。在Oracle 7和Oracle 8中,该参数缺省值为DB_BLOCK_BUFFERS/4的下一个素数。

通过以上的讨论可以知道,对应每个Bucket,只存在一个Chain,当用户试图搜索Cache Buffer Chain时,必须首先获得Cache Buffer Chain Latch。那么Cache BufferChain Latch的设置就同样值得研究了

 

我们看到,从Oracle  8i 开始,_db_block_hash_buckets 的数量较以前增加了8 倍,而_db_block_hash_latchs的数量增加有限,这意味着,每个Latch需要管理多个Bucket,但是由于Bucket数量的多倍增加,每个Bucket上的Block数量得以减少,从而使少量Latch管理更多Bucket成为可能。

 

 

 

总结一下上图中所描述的内容。

(1)  从Oracle8i开始,Bucket的数量比以前大大增加;通过增加的Bucket的“稀 释 ”使得每个Bucket上的Buffer数量大大减少。

(2)  在Oracle8i之前,_db_block_hash_latches的数量和Bucket的数量是一致的,每个Latch管理一个Bucket;从Oracle 8i开始每个Latch需要管理多个Bucket,由于每个Bucket上的Buffer数量大大降低,所以Latch的性能反而得到了提高。

(3)  每个Bucket存在一条Cache Buffer Chain。

(4)  Buffer Header上存在指向具体Buffer的指针。

 

 

2.X$BH与Buffer Header

Buffer  Header数据,可以从数据库的字典表中查询得到,这张字典表是:X$BH。X$BH中的BH就是指Buffer Headers,每个Buffer在x$bh中都存在一条记录

 

Buffer  Header中存储每个Buffer容纳的数据块的文件号、块地址、状态等重要信息,可以将Buffer Header看作是Buffer的“名片”,通过这张名片,Buffer的重要信息得以展现;

下图包含了Buffer Header上记录的信息,Hash Bucket上的Chian连接起这些BH。

 

 

 

根据Buffer  Header上记录的这些信息,结合dba_extents视图,可以很容易地找到每个Buffer对应的对象信息:

 

12:01:56 SQL>desc v$bh;

 Name                    Null?    Type

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

 FILE#                            NUMBER

 BLOCK#                           NUMBER

 CLASS#                           NUMBER

 STATUS                           VARCHAR2(10)

 XNC                              NUMBER

 FORCED_READS                     NUMBER

 FORCED_WRITES                    NUMBER

 LOCK_ELEMENT_ADDR                RAW(8)

 LOCK_ELEMENT_NAME                NUMBER

 LOCK_ELEMENT_CLASS               NUMBER

 DIRTY                            VARCHAR2(1)

 TEMP                             VARCHAR2(1)

 PING                             VARCHAR2(1)

 STALE                            VARCHAR2(1)

 DIRECT                           VARCHAR2(1)

 NEW                              CHAR(1)

 OBJD                             NUMBER

 TS#                              NUMBER

 LOBID                            NUMBER

 CACHEHINT                        NUMBER

 

12:07:19 SQL>

 

 

X$BH中还有一个重要字段TCH,TCH为Touch的缩写,表征一个Buffer的访问次数, Buffer被访问的次数越多,说明该Buffer越“抢手”,也就可能存在热点块竞争的问题。

以下通过几个简单的查询。以下查询用于获得当前数据库最繁忙的Buffer:

 

SELECT *

  FROM(SELECT addr, ts#, file#, dbarfil, dbablk, tch

         FROM x$bh

        ORDER BY tch DESC)

 WHERE ROWNUM < 11;

 

 

 

再结合dba_extents中的信息,可以查询得到这些热点Buffer都来自哪些对象:

查询热点块对象:

sys@felix SQL>SELECT e.owner, e.segment_name,e.segment_type

     FROMdba_extents e,

         (SELECT *

            FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch

                     FROM x$bh

                   ORDER BY tch DESC)

           WHERE ROWNUM < 11) b

    WHEREe.relative_fno = b.dbarfil

      ANDe.block_id <= b.dbablk

      AND e.block_id + e.blocks > b.dbablk;

 

OWNER                 SEGMENT_NAME                   SEGMENT_TYPE

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

SYS                   C_USER#                        CLUSTER

SYS                   I_USER1                        INDEX

SYS                   JOB$                           TABLE

SYS                   JOB$                           TABLE

SYS                   SCHEDULER$_CLASS               TABLE

SYS                   SCHEDULER$_CLASS_PK            INDEX

SYS                   SCHEDULER$_JOB                 TABLE

SYS                   SCHEDULER$_JOB                 TABLE

SYS                   SCHEDULER$_JOB                 TABLE

SYS                  SCHEDULER$_LIGHTWEIGHT_JOB    TABLE

 

10 rows selected.

 

12:16:13 sys@felix SQL>

 

除了查询X$BH之外,其实也可以从Buffer Cache的转储信息中,看到Buffer Header的具体内容,以下信息来自Level 1级的Buffer Dump:

*** ACTION NAME:() 2014-07-23 13:58:11.585

 

Dump of buffer cache at level 1 for tsn=2147483647rdba=0

BH (0x69fd83a8) file#: 2 rdba: 0x00804d72(2/19826) class: 4 ba: 0x69c08000

  set: 3pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25

  dbwrid: 0obj: 58298 objn: -1 tsn: 1 afn: 2 hint: f

  hash:[0x77f8d3b0,0x77f8d3b0] lru: [0x69fd85c0,0x69fd8360]

  lru-flags:debug_dump on_auxiliary_list

  ckptq:[NULL] fileq: [NULL] objq: [NULL] objaq: [NULL]

  st: CR md:NULL tch: 1

  cr: [scn:0x0.197ed5],[xid: 0x0.0.0],[uba: 0x0.0.0],[cls: 0x0.198787],[sfl: 0x0],[lc:0x0.0]

  flags:

BH (0x6a3f1e88) file#: 1 rdba: 0x004084d5(1/34005) class: 1 ba: 0x6a2bc000

  set: 3pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25

  dbwrid: 0obj: 37 objn: 37 tsn: 0 afn: 1 hint: f

  hash:[0x77f8d3c0,0x77f8d3c0] lru: [0x6a3f20a0,0x6a3f1e40]

  lru-flags:debug_dump hot_buffer

  ckptq:[NULL] fileq: [NULL] objq: [0x6a3f20c8,0x6a3f1e68] objaq:[0x6a3f20d8,0x6a3f1e78]

  st:XCURRENT md: NULL fpin: 'kdiwh100: kdircys' tch: 3

  flags:only_sequential_access

  LRBA:[0x0.0.0] LSCN: [0x0.0] HSCN: [0xffff.ffffffff] HSUB: [65535]

BH (0x6d7de638) file#: 2 rdba: 0x008109aa(2/68010) class: 4 ba: 0x6d4ae000

  set: 3pool: 3 bsz: 8192 bsi: 0 sflg: 1 pwc: 0,25

  dbwrid: 0obj: 71803 objn: -1 tsn: 1 afn: 2 hint: f

  hash:[0x77f8d3f0,0x77f8d3f0] lru: [0x6d7de5f0,0x6d7de850]

  lru-flags:debug_dump

  ckptq:[NULL] fileq: [NULL] objq: [NULL] objaq: [NULL]

  st: CR md:NULL tch: 1

  cr: [scn:0x0.197ed5],[xid: 0x0.0.0],[uba: 0x0.0.0],[cls: 0x0.199105],[sfl: 0x0],[lc:0x0.0]

  flags:

 

在Oracle 10g之前,数据库的等待事件中,所有Latch等待被归入Latch Free等待事件中,在Statspack的report中,如果在Top 5等待事件中看到Latch Free这一等待处于较高的位置,就需要DBA介入进行研究和解决。

 

3.热点块竞争与解决

接下来将通过一个环境的实际情况对热点块的问题进行分析和探讨。

Top 5等待事件都值得关注。注意到这个数据库中Latch Free是最严重的竞争。

由于Latch  Free是一个汇总等待事件,我们需要从v$latch视图获得具体的Latch竞争主要是由哪些Latch引起的。

如果cache buffers chains正是主要的Latch竞争。实际上,如果数据库在繁忙的时段,基本上处于停顿状态,大量进程等待latchfree竞争,这些获得Session的等待事件可以很容易地从v$session_wait视图中查询得到:

 

16:10:22 sys@felix SQL>select SID,EVENT,SEQ#from v$session_wait;

 

       SIDEVENT                                                             SEQ#

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

         1SQL*Net message to client                                          7166

         2pmon timer                                                        5913

         3rdbms ipc message                                                18352

         4 VKTM Logical Idle Wait                                               1

         5rdbms ipc message                                                 5912

         6DIAG idle wait                                                   17714

         7rdbms ipc message                                                 5923

         8DIAG idle wait                                                   17704

         9rdbms ipc message                                                 5913

        10rdbms ipc message                                                 9958

        11rdbms ipc message                                                11632

        12rdbms ipc message                                                22003

        13smon timer                                                          410

        14rdbms ipc message                                                   28

        15rdbms ipc message                                                 7335

        16rdbms ipc message                                                 17710

        18rdbms ipc message                                                  549

        20rdbms ipc message                                                  565

        21rdbms ipc message                                                  3404

        22rdbms ipc message                                                  546

        26Streams AQ: qmn coordinator idle wait                              1270

        28Streams AQ: waiting for time management or cleanup tasks              3

        30Streams AQ: qmn slave idle wait                                     712

        32rdbms ipc message                                                 5828

        33SQL*Net message from client                                         32

        35rdbms ipc message                                                 4902

        36SQL*Net message from client                                        7734

        37Space Manager: slave idle wait                                      125

        38Space Manager: slave idle wait                                      246

        40jobq slave wait                                                     54

        41jobq slave wait                                                     54

        45SQL*Net message from client                                         429

        47Space Manager: slave idle wait                                       65

 

33 rows selected.

 

16:10:30 sys@felix SQL>

 

如果需要具体确定热点对象,可以从v$latch_children中查询具体的子Latch信息:

SELECT *

  FROM(SELECT addr,

              child#,

              gets,

              misses,

              sleeps,

              immediate_gets   igets,

              immediate_misses imiss,

              spin_gets        sgets

         FROM v$latch_children

        WHERE NAME = 'cache buffers chains'

        ORDER BY sleeps DESC)

 WHEREROWNUM < 11;

_value,address, piece;

 

 

X$BH中还存在另外一个关键字段HLADDR,即Hash Chain Latch Address,这个字段可以和v$latch_child.addr进行关联,这样就可以把具体的Latch竞争和数据块关联起来,再结合dba_extents视图,就可以找到具体的热点竞争对象。

到具体热点竞争对象之后,可以进一步地结合v$sqlarea或者v$sqltext视图,找到频繁操作这些对象的SQL,对其进行优化,就可以缓解或解决热点块竞争的问题。通过以下查询可以实现以上的思想,获取当前持有最热点数据块的Latch及Buffer信息:

SELECT b.addr,

      a.ts#,

       a.dbarfil,

      a.dbablk,

      a.tch,

      b.gets,

      b.misses,

      b.sleeps

  FROM(SELECT *

         FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch, hladdr

                 FROM x$bh

                ORDER BY tch DESC)

        WHERE ROWNUM < 11) a,

      (SELECT addr, gets, misses, sleeps

         FROM v$latch_children

        WHERE NAME = 'cache buffers chains') b

 WHEREa.hladdr = b.addr;

 

ADDR                    TS#    DBARFIL    DBABLK        TCH       GETS    MISSES     SLEEPS

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

0000000077BF8A38          2          3        144        249       5274          0          0

0000000077FEF1F8          2          3        208        248       6702          0          0

0000000077F9FCF0          2          3        272        247       5198          0          0

0000000077FF0900          0          1       2017        246     15293          0          0

0000000077BD1018          2          3       176        245       5187          0          0

0000000077BE5128          2          3        160        245       5034          0          0

0000000077FC77D8          2          3        240        244       6431          5          3

0000000077BEEDB0          0          1       2016        244       9761          0          0

0000000077FDB0E8          2          3        224        244       6161          0          0

0000000077BBD708          2          3        192        242       5805          0         0

利用前面提到的SQL,可以找到这些热点Buffer的对象信息:

SELECT distinct e.owner, e.segment_name,e.segment_type

  FROMdba_extents e,

      (SELECT *

         FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch

                 FROM x$bh

                ORDER BY tch DESC)

        WHERE ROWNUM < 11) b

 WHEREe.relative_fno = b.dbarfil

   ANDe.block_id <= b.dbablk

   ANDe.block_id + e.blocks > b.dbablk;

 

16:18:29 sys@felix SQL>/

OWNER        SEGMENT_NAME                  SEGMENT_TYPE

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

SYS          _SYSSMU1_1240252155$          TYPE2 UNDO

SYS          _SYSSMU2_111974964$           TYPE2 UNDO

SYS          _SYSSMU5_4011504098$          TYPE2 UNDO

SYS          _SYSSMU9_3945653786$          TYPE2 UNDO

SYS          _SYSSMU6_3654194381$          TYPE2 UNDO

SYS          _SYSSMU4_1126976075$          TYPE2 UNDO

SYS          _SYSSMU8_3612859353$          TYPE2 UNDO

SYS          _SYSSMU10_3271578125$         TYPE2 UNDO

SYS          _SYSSMU3_4004931649$          TYPE2 UNDO

SYS          _SYSSMU7_4222772309$          TYPE2 UNDO

 

10 rows selected.

 

16:25:56 sys@felix SQL>

 

 

结合v$sqltext或v$sqlarea,可以找到操作这些对象的相关SQL,让我们继续查询:

 

 

break on hash_value skip 1

 

 

SELECT /*+ rule */

 hash_value,sql_text

  FROMv$sqltext

 WHERE(hash_value, address) IN

      (SELECT a.hash_value, a.address

         FROM v$sqltext a,

              (SELECT DISTINCT a.owner, a.segment_name, a.segment_type

                 FROM dba_extents a,

                       (SELECT dbarfil, dbablk

                          FROM (SELECT dbarfil,dbablk

                                  FROM x$bh

                                 ORDER BY tchDESC)

                         WHERE ROWNUM < 11)b

                WHERE a.relative_fno = b.dbarfil

                  AND a.block_id <= b.dbablk

                  AND a.block_id + a.blocks > b.dbablk) b

        WHERE upper(a.sql_text) LIKE '%' || b.segment_name || '%'

          AND b.segment_type = 'TABLE')

 ORDER BYhash_value, address, piece;

 

 

找到这些SQL之后,剩下的问题就简单了,可以通过优化SQL减少数据的访问,避免或优化某些容易引起争用的操作(如connect by等操作)来减少热点块竞争。

 

 

 

 

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值