Buffer Busy Waits是怎么产生的?

发布时间:2020-08-10 23:21:27 作者:wei-xh
来源:ITPUB博客 阅读:135

哪些场景会产生Buffer Busy Waits

Buffer Busy Waits是Oracle 数据库常见的一个等待,特别是在并发写比较频繁的环境里。作为一个Oracle DBA,如果你从未遇到过Buffer Busy Waits等待,那么你算不上一个真正的Oracle DBA。

产生这个等待的原因是,在内存级别,在同一时刻对同一内存块进行读写产生了争用。这里我以2个进程对数据块的操作为例,归纳出产生Buffer Busy Waits的场景:

  1. 写写,例如一个进程正在对内存块做更改,此时另一个进程也要修改这个内存块

  2. 写读,例如一个进程正在对内存块做更改,另一个进程此时想要读这个内存块。你可能会问,oracle为什么不去copy正在修改的块构造CR块,然后去读构造出的CR块,理由很简单,内存块正在写(变化)的过程中去做copy,是一个不安全的动作。

按照字符的排列组合,还剩”读读“、”读写“两个场景,这两个场景都不会导致产生Buffer Busy Waits(这里很重要的一个假设是2个进程),理由我们后面再给出,这里按住不表。

为什么要设计这个等待

Oracle为什么要设计这个等待,这是个好问题,本篇文章也主要是为了解答这个疑问。

“为什么”要比“是这样做的”的更重要,前者讲的是逻辑和洞见,后者描述的是过程和结果。

回答这个问题之前,首先设计一个场景,“假如没有这个等待,读写数据块是如何进行的”,当然这个场景是设计出来的,其实并不存在。

那我们开始,假设场景是这样子的,我们要对一个数据块做写入操作,遵循如下步骤:

  1. 首先依据数据块地址计算出(Hash算法)该块所在的Hash Bucket。

  2. 根据桶的编号,计算出保护这个桶的CBC Latch,然后申请CBC Latch,查找数据块在不在桶里,这里假设在内存里。

  3. 修改数据块。

  4. 释放CBC Latch。

以上的描述看似是非常通畅,但是存在一个问题,由于CBC Latch的持有是排他的,在持有CBC Latch的情况下,去修改数据块,那么这个Latch的持有时间就会比较长,这个长是相对于Latch的获取和释放这种CPU原子操作的,因此在持有CBC Latch的情况下修改数据块,对于读写频繁的数据库/块(热点块),那么势必会造成CBC Latch的争用,基于Latch的特性(自旋、休眠、再自旋),会造成大量CPU资源的浪费,导致数据库的性能低下。

为了解决这个问题,Oracle设计了Buffer Pin的功能。也就是说,Buffer Pin这个机制存在的价值,是为了降低CBC Latch的争用,节省CPU资源。

引入Buffer Pin后,修改数据块的大致步骤如下:

  1. 首先依据数据块地址计算出(Hash算法)该块所在的Hash Bucket。

  2. 然后申请CBC Latch,查找、定位到数据块

  3. 以X模式获取数据块的Buffer Pin。

  4. 释放CBC Latch

  5. 在Pin的保护下,修改数据块,完成后继续以下步骤

  6. 获得CBC Latch

  7. 释放Buffer Pin

  8. 释放CBC Latch

步骤复杂了许多,CBC Latch获取/释放共发生了两次,工作量似乎整整大了两倍。

可是极大程度降低了CBC latch的争用。 因为持有CBC Latch的时间变得极短。 持有CBC Latch,只是为了在buffer header上增加buffer pin,目的变得单纯和简单。

你可能会说,Oracle这种解决方案就是按了葫芦起了瓢,它只不过是转移了竞争,以前是CBC Latch的争用,现在是Buffer Pin的争用。

这句话并没错。

但是Buffer Pin的争用是不消耗CPU资源的。类似于队列锁的通知机制。而不会像Latch一样去做自旋。

读取数据块增加S模式的Buffer Pin,修改数据块增加X模式的Buffer Pin。S和S模式的Pin是兼容的,可以并发的读取,X和S模式是不兼容的,后来的读取会话需要产生等待。

文章的开头,我们举了2个进程操作同一内存块的例子,这里我们在有了一些知识之后,再做下总结和回顾。

我们平时经常说读不阻塞写,写不阻塞读,那是在物理的数据块级别,在内存里,在同一时刻对于同一个内存块的写读/写写都是互相阻塞的。

这里接着上面把场景补充完整,“读读”不会产生Buffer Busy Waits等待,因为Pin模式是兼容的。“读写”不会产生Buffer Busy Waits等待,一个进程正在读数据块,此时另一个进程要去写这个数据块,写进程很聪明,它会copy出一个current块出来(之前的块成为CR块),然后在current块上进行写操作,通过这种方式避免了竞争,规避了Buffer Busy Waits的争用。

因为前一个进程是在读取,而不是修改。这个场景下的copy就是安全的。

另外一点,因为读取数据库块需要在Buffer header上增加S模式的Buffer Pin,属于内存的修改操作,因此即使是读取操作,CBC Latch的持有也是排他的,但是这个增加Pin的时间极短,在压力正常的数据库环境中引起CBC Latch大量争用的可能性不大。

当然如果是大量进程对同一内存块频繁读取,就会引起CBC Latch的争用。

一些细节

最后,有必要对一些细节再做些补充:

总结

本文讲述了Buffer Busy Waits是如何产生的及其机制,但是并没有讲解如何对其进行调优。Buffer Busy Waits等待设计的本质是为了降低CBC Latch的争用。以两个进程操作内存块为例,在“写写”、“写读”场景下会产生Buffer Busy Waits,在“读读”场景下不会产生Buffer Busy Waits等待,在“读/写”场景下,发生写操作的服务器进程会去Copy出一个current块来继续写操作,而不会去等待Buffer Busy Waits。


推荐阅读:
  1. Oracle 等待事件之 free buffer waits
  2. Oracle 等待事件之 buffer busy waits

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

buffer busy waits

上一篇:XMPP

下一篇:关于JSF与Struts的区别

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》