buffer busy waits理解

一.什么是buffer busy waits

当n个进程想以不兼容的模式持有内存块上的buffer pin时,就会产生buffer busy waits等待。

oracle访问/修改数据块的步骤:

1.依据数据块的地址计算出数据块所在的bucket

2.获得保护这个bucket的cbc latch

3.在这个链表上查找需要的数据块,找到后,pin这个buffer(读取s,修改x)

4.释放cbc latch

5.读取/修改数据块的内容

6.获取cbc latch

7.unpin这个buffer

8.释放cbc latch

分析:因为latch的操作都是很短的,上面步骤中除了步骤5,都可以认为是极快的操作。在大并发环境中,如果cbc latch的持有时间过长,会导致大量的latch争用,以致非常容易导致系统的cpu资源出现瓶颈。注意即使所有的操作都是查询非修改,也会导致大量的cbc latch争用:因为cbc latch的持有到cbc latch的释放这段时间太长了。oracle使用在内存块上加buffer pin的方式来解决这个问题。可以看出,这种实现方式使得cbc latch持有期间只做了很少的事情,大大降低了cbc latch的争用。

如果数据库里面读多写少,由于各个读之间的buffer pin时兼容的,都是s模式,因此几乎不会产生任何的争用。

如果数据库里面写多读少,就会产生buffer busy waits等待,但这种等待的代价比cbc latch的等待代价要小的多,因为latch的spin机制是非常耗cpu的,而buffer pin的管理本质上类似于enquen锁的机制,没有spin机制,不需要自旋转消耗大量的cpu。

二.buffer busy waits测试

创建测试表:

SQL> create table test_buffer_busy as select * from dba_objects;

Table created.

SQL> create index idx_test_buffer_1 on test_buffer_busy(object_id);

Index created.

找到同一个块上的两条记录:

SQL> select dbms_rowid.ROWID_RELATIVE_FNO(rowid) fn,dbms_rowid.rowid_block_number(rowid) bl, test_buffer_busy.object_id,rowid from test_buffer_busy where rownum<3;

        FN         BL  OBJECT_ID ROWID
---------- ---------- ---------- ------------------
         4       3131         20 AAAVXOAAEAAAAw7AAA
         4       3131         46 AAAVXOAAEAAAAw7AAB

1.场景1,读读操作

session1执行:

SQL> select sid from v$mystat where rownum=1;

       SID
----------
         1

SQL> declare
  2  c number;
  3  begin
  4  for i in 1 ..6000000 loop
  5  select count(*) into c from test_buffer_busy where rowid='AAAVXOAAEAAAAw7AAA';
  6  end loop;
  7  end;
  8  /

session2执行:

SQL> select sid from v$mystat where rownum=1;

       SID
----------
        37

SQL> declare
  2  c number;
  3  begin
  4  for i in 1 ..6000000 loop
  5  select count(*) into c from test_buffer_busy where rowid='AAAVXOAAEAAAAw7AAB';
  6  end loop;
  7  end;
  8  /

sesson3观察是否产生buffer busy waits等待事件:

SQL> select event,sid,p1,p2,p3 from v$session_wait where sid in (1,37) and event like '%buffer%';

no rows selected

结论:读读场景不会产生任何buffer busy waits等待事件

2.场景2,写写操作

session1:

SQL> begin  
for i in 1 ..40000000 loop
  2    3  UPDATE test_buffer_busy SET object_name=20 where rowid='AAAVXOAAEAAAAw7AAA';
  4  commit;
  5  end loop;
  6  end;
  7  /

session2:

SQL> begin
  2  for i in 1 ..40000000 loop
  3  UPDATE  test_buffer_busy SET object_name=46 where rowid='AAAVXOAAEAAAAw7AAB';
  4  commit;
  5  end loop;
  6  end;
  7  /

session3:

SQL> select event,sid,p1,p2,p3 from v$session_wait where sid in(46,50) and event like '%buffer%';

no rows selected
EVENT                                                                   SID
---------------------------------------------------------------- ----------
        P1         P2         P3
---------- ---------- ----------
buffer busy waits                                                        46
         4       3131          1

buffer busy waits                                                        50
         4       3131          1
参数解释:

p1:数据块驻留的绝对文件号。select file_name,relative_fno from dba_data_files;

p2:进程需要访问的实际数据块号。

p3:块类型编号

1  Data block

2  Sort block

3  Save undo block

4  Segment header

5  Save undo header

6  Free List

7  Extent map

8  1st level bitmap block

9  2nd level bitmap block

10 3rd levelbitmap block

11 Bitmapblock

12 Bitmapindex block

13 Fileheader block

14 Unused

15 Systemundo block

16 Systemundo block

17 Undoheader

18 Undoblock

查看v$waitstat视图:

SQL> select * from v$waitstat;

CLASS                   COUNT       TIME
------------------ ---------- ----------
data block              10349       2399
可以看到data block上面发生了大量等待。

两个session的等待里都有大量的buffer busy waits等待,由于session1和session2在同一个buffer上加x排他的buffer pin,两种锁模式的不兼容导致了争用。

3.场景3,读写操作

session1:

SQL> begin
  2  for i in 1..40000000 loop
  3  UPDATE test_buffer_busy SET object_name=20 where rowid='AAAVXOAAEAAAAw7AAA';
  4  commit;
  5  end loop;
  6  end;
  7  /

session2:

SQL> declare
  2  c number;
  3  begin
  4  for i in 1..6000000 loop
  5  select count(*)into c from test_buffer_busy where rowid='AAAVXOAAEAAAAw7AAB';
  6  end loop;
  7  end;
  8  /

session3:

SQL> select event,sid,p1,p2,p3 from v$session_wait where sid in (1,37) and event like '%buffer%';

EVENT                                                                   SID
---------------------------------------------------------------- ----------
        P1         P2         P3
---------- ---------- ----------
latch: cache buffers chains                                               1
2362473448        177          0

buffer busy waits                                                        37
         3        272         35


可以看到,session1没有任何的buffer busy waits等待,而进行读操作的session2,产生了大量的buffer busy waits等待。原理:

(1).当读取的进程发现内存块正在被修改时(x模式的buffer pin),它只能等待而不能clone,因为这个时候内存块正在变化过程中,这个时候clone是不安全的。这个时候,读的进程只能等待buffer busy waits。

(2).当写的进程发现内存块正在被读,这个时候,读时不阻塞写的,因为oracle可以很容易的clone出一个数据块,然后在clone的块上进行写,这个时候clone是安全的,因为读内存块的进程不会去修改数据块,保证了clone的安全性。

三.总结

1.buffer busy waits是产生在buffer block上的等待,由于n个进程想以不兼容的模式获得buffer block上的buffer pin时,进而引起buffer waits等待。

2.buffer block的管理模式类似enq锁,先进先出,由队列记录锁的拥有者和等待者

3.写写,读写操作都会产生buffer busy waits等待。写写的两个会话,都会产生buffer waits等待,而读写的两个会话,只有读的session会产生,因为它不能简单的clone一个数据块,正在发生写的内存块发生clone是不安全的。

4.oracle为了减少cbc latch持有时间过长的问题,以每次访问buffer block的会话获取两次cbc latch,再配合在内存块上加buffer pin来解决这个问题。






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值