通过案例学调优之--和 BUFFER CACHE 相关的主要 Latch

2.1、和 BUFFER CACHE 相关的主要 Latch 有:
Latch:cache buffers lru chain
Latch: cache buffers chains既然Latch是用来保护共享内存块不被并行破坏,那就了解BUFFER CACHE的相关原理,进 而得知什么时候需要用到这个Latch,才能够有相应的解决方案。

2.2BUFFER CACHE 是用来缓存数据块的地方,那么数据的查询和修改都要通过它来完成。 接下来就看看访问数据的流程是怎样的:


2.2.1当一个进程要访问数据时,首先要查找 BUFFER CACHE 看看数据是否已经存在?

(Y) 如果数据在 BUFFER CACHE 中存在,则根据数据的状态来判断是否可以直接访问 还是需要构造一致性读取?

(N) 如果数据在 BUFFER CACHE 中不存在,则需要从数据文件中读取数据块到 BUFFER CACHE 中去。这个时候,需要查找在 BUFFER CACHE 中寻找足够的内存空间来读取相关的数 据。
2.2.2现在 BUFFER CACHE 那么大,一个 BUFFER 一个 BUFFER 的扫描过去是相当的消耗资源以及查 询的时间,那我们怎样才能快速地查到数据了?
可以看看下面关于 
BUFFER CACHE 的示图:

wKioL1RjAt2g9T7GAAIE1X17zB8435.jpg

这边我们先看下面几点:
1) 图中右边的有一块 Buffers Memory,其中每一块小格就是一个 Buffer(用来存放从数据文

件中读取的数据块 Block)

2)  图中左边的有许多 Buffer Header 用虚线指向 Buffers Memory 中的相应的 Buffer

3)  图中左边的有许多实线箭头,这些箭头(其实就是数据结构的链表结构中的指针)将不 同的 Buffer Header 连接成一条 Hash Chain,这边也就是 Cache Buffers Chain(双向链表)。

4)  另外,还有一个 Hash Bucket,其实这只是一个逻辑上的概念,即每一个 Hash Bucket 都 会有一条 Hash Chain 来将 Buffer Header(按照 HASH 算法分类后)连接起来,并由一个Cache Buffers Chains Latch 来进行管理其并发操作。

每当将一个Block读入到Buffer Cache的时候,首先会构造一个与之对应的Buffer Header, 然后根据 HASH 算法( Hash Bucket = MOD(Data Block Address, _DB_BLOCK_HASH_BUCKETS) ), 将 Buffer Header 放到对应的 Hash Bucket 的 Cache Buffers Chain 中去,并在 Buffer Header 中 存放如下信息:

wKiom1RjA7KQLSatAAQalv6MWBk606.jpg

5) 最后,在看看 Hash Latch,即 Cache Buffers Chains Latch,在 Oracle 8i 之前,对应每一个Hash Bucket,Oracle使用一个独立的Latch来维护。Oracle 8i开始,Oracle增加了Hash Bucket 的数量,这样每个 Latch 需要维护多个 Bucket,由于每个 Hash Bucket 上的 Buffer Header 数量大大减低,也使得Latch 的性能反而提高。其中,BUFFER CACHE 中 Hash Bucket的个数由隐含参数_db_block_hash_buckets 决定,Cache Buffers Chains Latch 的个数由隐 含参数_db_block_hash_latches 决定。

2.2.3现在再回过头,看看怎样查找 BUFFER CACHE 看看数据是否已经存在?

(Y) 如果数据在 BUFFER CACHE 中存在的情况。
1) 根据要查找的数据块的 DBA 等信息,通过上面给的 HASH 算法( Hash Bucket = MOD(Data

Block Address, _DB_BLOCK_HASH_BUCKETS) ),得到该数据块所在的 Hash Bucket
2) 定位到对应的 Hash Bucket 上,在该 Hash Bucket 对应的 Cache Buffers Chain 中加上 Cache Buffers Chains Latch,然后从 Cache Buffers Chain 对应的第一个 Buffer Header 开始扫描查 找,直至之后一个。在这个扫描查找过程中,为防止对 Cache Buffers Chain 并发访问,

将一直持有 Cache Buffers Chains Latch
在 
Cache Buffers Chain 上查找的具体逻辑如下:


wKiom1RjBKuDVLZ1AAQnHjYfDE4773.jpg

3)获得第 2)步中查到的可以直接访问的 Buffer Header 或者构造一致性读后的 Buffer Header 中的 Buffer 地址到 Buffer Memory 来获得数据。

2.2.4 如果上面在Hash Chain中查找不到对应的Buffer Header的情况下,也就前面的问题的第二 种情况:

(N) 如果数据在 BUFFER CACHE 中不存在,则需要从数据文件中读取数据块到 BUFFER CACHE 中去。这个时候,需要查找在 BUFFER CACHE 中寻找足够的内存空间来读取相关的数 据。
再看看前面给的 
BUFFER CACHE 示例图:
可以看到 
条链(LRU 和 LRUW),这 条分别将 Buffer Header 连接起来,和 Cache Buffers Chain类似。下面看看这 条链的作用:
1)LRU 表示 Least Recently Used,也就是指最近最少使用的 Buffer Header 链表。LRU 链表串 连起来的 Buffer Header 都指向可用数据块(Free Buffer)。
2)LRUW 则表示 Least Recently Used Write,也叫做 Dirty List,也就是脏数据块链表,LRUW串起来的都是修改过但是还没有写入数据文件的内存数据块所对应的 Buffer Header(Dirty Buffer)。
3)一个 Buffer Header 要么在 LRU 上,要么在 LRUW 上,不能同时存在于这两个链表上。

所以,当前面查找数据在 BUFFER CACHE 中不存在的时候(即在 Hash Chain 中查找不到对应 的 Buffer Header 的情况下):
1) 就要扫描 LRU List 寻找 Free 的 Buffer,在扫描过程将持有 Cache Buffers Lru Chain Latch(其

Latch 数量由隐含参数_db_block_lru_latches 决定)。扫描过程中会把已经被修改过的

Buffer 移动到 LRUW 列表上。
2) 找到足够的 Buffer 后,将数据块 Block 读入到 Buffer Cache,构造一个与之对应的 Buffer

Header , 然 后 根 据 HASH 算 法 ( Hash Bucket = MOD(Data Block Address, _DB_BLOCK_HASH_BUCKETS) ),将 Buffer Header 放到对应的 Hash Bucket 的 Cache Buffers Chain 中去,并在 Buffer Header 中存放相关的信息。

2.3、总的在 BUFFER CACHE 中查找数据,可以简单的表示为下图:

wKiom1RjBU6CihCvAAJ69aBhzbI937.jpg

2.4 通常有 中情况或导致 Latch 出现竞争:
1) 某一进程长时间的持有 Latch,导致其他进程不能正常的得到 Latch,只能等待。
2) 可能存在大量的 Latch 请求。
2.4.1如果出现 Cache Buffers Chains Latch 竞用严重,根据前面的原理,那么可能有如下原因:1)当多个会话重复访问一个或多个由同一个子Cache Buffers Chains Latch保护的块时热点
块(可以关注 X$BH 中的 TCH 字段)。

2) 大量并发执行的低效 SQL。低效的 SQL 通常需要获取大量的逻辑读,而得到一次逻辑 IO

就得获得一次 Cache Buffers Chains Latch
3Hash Bucket 中存在长的 Cache Buffers Chains,导致查询数据时候,长时间持有 Latch2.4.2
如果出现 Cache Buffers Lru Chain Latch 竞用严重,那么可能有如下原因:
1) 可能 BUFFER CACHE 分配的空间不够,导致读数据到 BUFFER CACHE 的时候,不断的扫描
LRU List

案例分析:

2.5、测试:模拟 Cache Buffers Chains Latch 竞用 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
1 、建立测试table,只有一条记录
14 : 10 : 47  SCOTT@ prod >select *  from  test;
          N
----------
         10
 
2 、查看表存储的block信息        
14 : 10 : 50  SCOTT@ prod >select dbms_rowid.rowid_relative_fno(rowid) file#,dbms_rowid.rowid_block_number(rowid) block#  from  test;
      FILE#     BLOCK#
---------- ----------
          6         365
 
3 、建立测试存储过程
14 : 16 : 29  SCOTT@ prod >create  or  replace  procedure test_latch is
14 : 16 : 30    2      i number;
14 : 16 : 30    3     begin
14 : 16 : 30    4       loop
14 : 16 : 30    5        select n  into  from  test;
14 : 16 : 30    6     end loop;
14 : 16 : 30    7     end;
14 : 16 : 31    8   /
Procedure created.
 
4 、建立session测试
14 : 17 : 14  SCOTT@ prod >select *  from  v$mystat  where  rownum= 1 ;
        SID STATISTIC#      VALUE
---------- ---------- ----------
          1           0           0
  
5 、执行procedure进行测试         
14 : 17 : 27  SCOTT@ prod >exec test_latch;
 
6 、查看会话信息(event)
14 : 20 : 38  SYS@ prod > select sid,event,p1,p1raw,p1text,p2,p2text  from  v$session  where  sid= 1
        SID EVENT                                  P1 P1RAW            P1TEXT                       P2 P2TEXT
---------- ------------------------------ ---------- ---------------- -------------------- ---------- --------------------
          1  SQL*Net message  from  client     1650815232  0000000062657100  driver id                      1  #bytes
 
7 、在执行期间可以看到 Cache Buffers Lru Chain Latch 都是 Get 成功,不存在竞争     
14 : 21 : 13  SYS@ prod >select gets,misses,sleeps,spin_gets,wait_time  from  v$latch
14 : 21 : 49    2       where  name= 'cache buffers chains' ;
 
       GETS     MISSES     SLEEPS  SPIN_GETS  WAIT_TIME
---------- ---------- ---------- ---------- ----------
   63272697           0           0           0           0
 
8 、再建立另一个session,执行相同的任务
14 : 23 : 07  SYS@ prod >conn scott/tiger
Connected.
14 : 23 : 09  SCOTT@ prod >select *  from  v$mystat  where  rownum= 1 ;
 
        SID STATISTIC#      VALUE
---------- ---------- ----------
         56           0           0
 
14 : 23 : 22  SCOTT@ prod >exec test_latch;
        
9 、查看session event             
1 )可以看到出现latch: cache buffers chains等待事件。这就是 2 个session要同时访问同一
个 Block,这个时候在一个会话持有 Latch 的时候,另一个会话必须 Spin 等待获得 Latch。
2 )同时也能看到 cursor: pin S等待事件。 
         
14 : 23 : 42  SYS@ prod >select sid,event,p1,p1raw,p1text,p2,p2text  from  v$session  where  sid  in  ( 1 , 56 );
        SID EVENT                                  P1 P1RAW            P1TEXT                       P2 P2TEXT
---------- ------------------------------ ---------- ---------------- -------------------- ---------- --------------------
          1  latch: cache buffers chains      850140044  0000000032AC1B8C address                      150  number
         56  cursor: pin S                   3879408699  00000000E73B143B idn                        65536  value
 
14 : 24 : 34  SYS@ prod >/
        SID EVENT                                  P1 P1RAW            P1TEXT                       P2 P2TEXT
---------- ------------------------------ ---------- ---------------- -------------------- ---------- --------------------
          1  latch: cache buffers chains      850082532  0000000032AB3AE4 address                      150  number
         56  latch: cache buffers chains      850140044  0000000032AC1B8C address                      150  number
 
14 : 24 : 46  SYS@ prod >/
        SID EVENT                                  P1 P1RAW            P1TEXT                       P2 P2TEXT
---------- ------------------------------ ---------- ---------------- -------------------- ---------- --------------------
          1  latch: cache buffers chains      849991208  0000000032A9D628 address                      150  number
         56  latch: cache buffers chains      850231368  0000000032AD8048 address                      150  number
         
14 : 38 : 32  SYS@ prod >/
 
        SID EVENT                                  P1 P1RAW            P1TEXT                       P2 P2TEXT
---------- ------------------------------ ---------- ---------------- -------------------- ---------- --------------------
          1  cursor: pin S                   3879408699  00000000E73B143B idn                      3670016  value
         56  cursor: pin S                   3879408699  00000000E73B143B idn                        65536  value
10 、这个时候可以看到 Cache Buffers Lru Chain Latch 都是 Get 出现严重的竞争,出现大量的Misses,Sleeps,Spin_gets:             
14 : 24 : 48  SYS@ prod >select gets,misses,sleeps,spin_gets,wait_time  from  v$latch
14 : 25 : 06    2   where  name= 'cache buffers chains' ;
       GETS     MISSES     SLEEPS  SPIN_GETS  WAIT_TIME
---------- ---------- ---------- ---------- ----------
  111686729        2358        1293        1065     1043230
Elapsed:  00 : 00 : 00.00
14 : 25 : 18  SYS@ prod >/
       GETS     MISSES     SLEEPS  SPIN_GETS  WAIT_TIME
---------- ---------- ---------- ---------- ----------
  115457171        2667        1466        1202     1178935
Elapsed:  00 : 00 : 00.00
14 : 25 : 33  SYS@ prod >/
       GETS     MISSES     SLEEPS  SPIN_GETS  WAIT_TIME
---------- ---------- ---------- ---------- ----------
  115987352        2722        1497        1226     1204103
Elapsed:  00 : 00 : 00.00

查看CPU资源信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[oracle@RH6 ~]$ top
top -  14 : 42 : 57  up   2 : 20 ,   4  users,  load average:  2.12 2.14 1.66
Tasks:  177  total,    3  running,  174  sleeping,    0  stopped,    0  zombie
Cpu(s):  67.1 %us,  32.6 %sy,   0.0 %ni,   0.0 %id,   0.0 %wa,   0.3 %hi,   0.0 %si,   0.0 %st
Mem:   1207500k total,  1143680k used,    63820k free,   101824k buffers
Swap:  2064376k total,        0k used,  2064376k free,   778420k cached
   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  6410  oracle     20    0   471m  58m  55m R  49.3   4.9   15 : 07.16  oracle
  6604  oracle     20    0   468m  20m  18m R  49.0   1.7    9 : 31.20  oracle
  1887  oracle     -2    0   468m  12m  11m S   1.0   1.1    0 : 23.97  oracle
  1937  oracle     20    0   483m  13m  12m S   0.3   1.2    0 : 00.06  oracle
     1  root       20    0   2828  1396  1196  S   0.0   0.1    0 : 00.81  init
     2  root       20    0      0     0     0  S   0.0   0.0    0 : 00.00  kthreadd
     3  root      RT    0      0     0     0  S   0.0   0.0    0 : 00.00  migration/ 0
     4  root       20    0      0     0     0  S   0.0   0.0    0 : 00.02  ksoftirqd/ 0
     5  root      RT    0      0     0     0  S   0.0   0.0    0 : 00.00  watchdog/ 0
     6  root       20    0      0     0     0  S   0.0   0.0    0 : 00.12  events/ 0
     7  root       20    0      0     0     0  S   0.0   0.0    0 : 00.00  cpuset

内存信息:

1
2
3
4
5
[oracle@RH6 ~]$ vmstat  2  2
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
  r  b   swpd   free   buff  cache   si   so    bi    bo    in    cs us sy id wa st
  3   0       0   69756  101928  778428   108     0      0     92   349   321  13   7  73   7   0
  2   0       0   69748  101928  778428    64     0      0      0  1208   539  63  37   0   0   0

附注:

Cursor: Pin S等待,这是一个由于频繁执行SQL共享解析时产生的竞争。当一个会话尝试以共享模式(S - Share)来获得一个游标时,需要修改相应的Mutex结构的引用计数(reference count),或者增加该计数,或者减少。修改引用技术的原子操作很快(其实和Latch的获取释放类似),但是在频繁解析的情况下,仍然产生了竞争和等待,由此就产生了 cursor : pin S 的等待。

这通常是由于某些SQL以超高频繁的频率执行导致的,当然也可能与系统的CPU能力不足有关。

Mutex机制在Oracle 10g引入,用于替代Library cache pin操作,其性能更高,其原理为在每个Child Cursor上分配一个地址空间记录Mutex,当该Cursor被共享执行时,通过将该位进行加一处理来实现。虽然是指游标共享,但是更新Mutex结构的操作需要排他,当某一个SQL被频繁共享执行时,可能就会出现Pin S的等待。

每个Library Cache对象都有一个reference count (引用计数),用来表明有多少其他对象目前正保留一个对它的引用(reference).  对象A 想要引用对象B, A 就把B 的 reference count 加 1。 当A 结束了对B 的引用, A 就把 B 的reference count 减 1.   当没有任何对象再引用 B 时, B 的 reference count就减为0, B 就被清除(deallocated), 内存就被释放。清除B的时候, 被B所用的对象的 reference count 也可能减小, 也可能使它们被清除。

最简单的解决方案是,将频繁执行的SQL分区拆解,分散竞争,如以下SQL通过注释将同一条SQL分解为2条,就分散了竞争:
    select /*SQL 1*/ a from t_a where id=?

    select /*SQL 2*/ a from t_a where id=?
这种做法在Ebay、Papal、支付宝等公司被广泛采用。

在官方文档上这样解释:

A session waits for "cursor: pin S" when it wants a specific mutex in S (share) mode on a specific cursor and there is no concurrent X holder but it could not acquire that mutex immediately. This may seem a little strange as one might question why there should be any form of wait to get a mutex which has no session holding it in an incompatible mode. The reason for the wait is that in order to acquire the mutex in S mode (or release it) the session has to increment (or decrement) the mutex reference count and this requires an exclusive atomic update to the mutex structure itself. If there are concurrent sessions trying to make such an update to the mutex then only one session can actually increment (or decrement) the reference count at a time. A wait on "cursor: pin S" thus occurs if a session cannot make that atomic change immediately due to other concurrent requests. Mutexes are local to the current instance in RAC environments.











本文转自 客居天涯 51CTO博客,原文链接:http://blog.51cto.com/tiany/1575694,如需转载请自行联系原作者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值