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]  view plain  copy
  1. SQL> create table test_buffer_busy as select * from dba_objects;  
  2.   
  3. Table created.  
  4.   
  5. SQL> create index idx_test_buffer_1 on test_buffer_busy(object_id);  
  6.   
  7. Index created.  

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

[sql]  view plain  copy
  1. 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;  
  2.   
  3.         FN         BL  OBJECT_ID ROWID  
  4. ---------- ---------- ---------- ------------------  
  5.          4       3131         20 AAAVXOAAEAAAAw7AAA  
  6.          4       3131         46 AAAVXOAAEAAAAw7AAB  

1.场景1,读读操作

session1执行:

[sql]  view plain  copy
  1. SQL> select sid from v$mystat where rownum=1;  
  2.   
  3.        SID  
  4. ----------  
  5.          1  

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

session2执行:

[sql]  view plain  copy
  1. SQL> select sid from v$mystat where rownum=1;  
  2.   
  3.        SID  
  4. ----------  
  5.         37  

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

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

[sql]  view plain  copy
  1. SQL> select event,sid,p1,p2,p3 from v$session_wait where sid in (1,37) and event like '%buffer%';  
  2.   
  3. no rows selected  

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

2.场景2,写写操作

session1:

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

session2:

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

session3:

[sql]  view plain  copy
  1. SQL> select event,sid,p1,p2,p3 from v$session_wait where sid in(46,50) and event like '%buffer%';  
  2.   
  3. no rows selected  
  4. EVENT                                                                   SID  
  5. ---------------------------------------------------------------- ----------  
  6.         P1         P2         P3  
  7. ---------- ---------- ----------  
  8. buffer busy waits                                                        46  
  9.          4       3131          1  
  10.   
  11. buffer busy waits                                                        50  
  12.          4       3131          1  
参数解释:

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

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

p3:块类型编号

[html]  view plain  copy
  1. 1  Data block  
  2.   
  3. 2  Sort block  
  4.   
  5. 3  Save undo block  
  6.   
  7. 4  Segment header  
  8.   
  9. 5  Save undo header  
  10.   
  11. 6  Free List  
  12.   
  13. 7  Extent map  
  14.   
  15. 8  1st level bitmap block  
  16.   
  17. 9  2nd level bitmap block  
  18.   
  19. 10 3rd levelbitmap block  
  20.   
  21. 11 Bitmapblock  
  22.   
  23. 12 Bitmapindex block  
  24.   
  25. 13 Fileheader block  
  26.   
  27. 14 Unused  
  28.   
  29. 15 Systemundo block  
  30.   
  31. 16 Systemundo block  
  32.   
  33. 17 Undoheader  
  34.   
  35. 18 Undoblock  

查看v$waitstat视图:

[sql]  view plain  copy
  1. SQL> select * from v$waitstat;  
  2.   
  3. CLASS                   COUNT       TIME  
  4. ------------------ ---------- ----------  
  5. data block              10349       2399  
可以看到data block上面发生了大量等待。

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

3.场景3,读写操作

session1:

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

session2:

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

session3:

[sql]  view plain  copy
  1. SQL> select event,sid,p1,p2,p3 from v$session_wait where sid in (1,37) and event like '%buffer%';  
  2.   
  3. EVENT                                                                   SID  
  4. ---------------------------------------------------------------- ----------  
  5.         P1         P2         P3  
  6. ---------- ---------- ----------  
  7. latch: cache buffers chains                                               1  
  8. 2362473448        177          0  
  9.   
  10. buffer busy waits                                                        37  
  11.          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来解决这个问题。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值