Oracle Free Buffer Waits

等待事件free buffer waits与数据库写进程的活动紧密相关并且通常也会看到等待事件db file parallel write与log file parallel write(日志写进程等待)。然而free buffer waits等待事件是唯一要数据库写进程,IO子系统和服务器进程活动有趣的给合才能出现。

理解free buffer waits的关键是要了解push-to-disk问题与pull-from-LRU-chain问题之间的区别。当我们看到等待事件db file parallel write进入到报告的top等待事件中时,这个问题说明有太多的时间被花费在写或pushingdirty buffers到磁盘上了(buffers不是真实的移动到磁盘,而是复制,将造成块与buffer匹配来创建free buffer)。一个服务器进程当它不能足够快速地找到一个free buffer时就会posts一个free buffer waits事件。一种原因是数据库写进程没有从LRU chian中pulled足够的dirty buffers并且使它们再次变为free状态。因此它不是一个push-to-disk的问题,而是一个pull-from-LRU-chain问题。虽然这种差异可能被夸大了,但它对我们的解决方法有很大的影响。

如果我们关注pull问题,那么将尽一切可能确保数据库写进程不会落后于从buffer cache中获取dirty buffers太多。换句话说,我们的目标是让数据库写进程做更多的工作因为问题不是写入磁盘的问题。如果问题是写入磁盘的问题,那么top等待事件将是db db file parallel write而不是free buffer waits。

通过另一种buffer cache场景来突出显示free buffer wait的情况。假设你是一个服务器进程必须要查询一行记录,而记录存放在一个特定的数据块中。基于SQL语句与数据字典,你知道文件号与块号。你肯定希望这个数据块已经存放到buffer cache中了,但为了确认并获得它的内存地址,你必须访问CBC结构。通过哈希文件号与块号指向到一个特定的哈希桶。基于这个哈希桶,查找相关的latch并争取拿到这个latch。在经过一些spins操作后,可能能获得这个latch,因此可以开始序列化CBC搜索操作。然而这个CBC是空的并且没有包含buffer headers。因此知道这个数据块不在buffer cache中。为了得到这个数据块,给IO子系统执行读取调用并等待,将post一个db file squential read。最后,获得了这个数据块,停止等待,并且开始消耗CPU,现在这个数据块已经到了你的PGA内存中。

你需要找到一个free buffer来缓存这个块,因此要定位你的LRU chain。但在你可以开始扫描你的LRU chain之前,你必须要获得合适的LRU chain latch。最后使用你的LRU chain latch与在PGA内存中的块,你开始扫描LRU来查找一个free buffer。你首先遇到一个buffer header并检查这个buffer header。好消息是这个buffer是free状态,但是坏消息是它不被频繁访问,且touch count为12。因此promote这个buffer到LRU列表的MRU端,并且将touch count减小到0。

继续查找到下一个buffer header,检查buffer header并发现它是一个dirty buffer且touch count为1。不满足缺省频繁访问的阈值2。因此,移动buffer header到LRU chain’s写列表。在完成移动后,检查drity list长度确保
它小于_db_large_dirty_queue的值。dirty list只有6,它小于缺省值25,因此不需要通知数据库写进程执行写操作。

现在假设你已经扫描了比_db_writer_max_scan_pct更多的buffer headers。如果是这样将会很沮丧。将会要消耗大量的CPU与持有LRU chain latch相当长的时间。假设你已经扫描的buffer headers比这个阈值多,你现在停止扫描,释放LRU chain latch,通知数据库写进程释放一些buffers,并post等待事件free buffer waits而耐心等待10ms。当你正在感叹“free buffer wait!”有10ms时,数据库写进程正在忙于将dirty buffer写入磁盘,并释放它们,然后将它们再插入到LRU chain的LRU端。

现在已经等待了10ms,你被唤醒,再次获取LRU chain latch,并开始从LRU chain的LRU端搜索。现在很有可能有一个不被频繁访问的free buffer正在等待你执行替换操作。现在pin住buffer header,释放LRU latch,更新buffer header,合理移动CBC结构中的buffer header因此在你正将这个块放入cache时其它的进程可以找到这个块,使用从磁盘中读取的块来替换free buffer,然后unpin这个buffer header。

注意是什么导致服务器进程post这个free buffer wait事件了。首先,执行一个物理IO读取,将强制服务器进程来搜索一个free buffer。再次,需要扫描太多的dirty buffers,这意味着必须存在活动的DML语句。最后,数据库写进程没能确保在LRU chain的LRU端有足够的free buffers。这三种条件都会造成这种情况,这也意味着对于这个问题有三种解决方案。

如果top等待事件是free buffer waits,关注pull,而不是push问题。如果忘记这一点,将会采用不合适的解决方案。

操作系统可能会遇到CPU或IO瓶颈或都两者都有,但可能是IO瓶颈。等待事件free buffer waits从来没有通过关注操作系统而被解决。应该从Oracle与应用程序角度来解决这个问题。如果是CPU瓶颈,查找非Oracle消耗CPU的进程。
这里有以下解决方案:
.增加buffer cache
如果有可用内存,增加buffer cache大小。这将允许更多的buffers可用,将增加找到一个free buffer的可能性。

.增加数据库写进程的pull能力
例如增加数据库写进程的数量。做任何你认为可以帮助数据库写进程可以增加dirty buffer写入效率的事情。除非buffer cache非常小,那么这可能是你最好的解决方案了。

.增加_db_writer_max_scan_pct参数
这将给数据库写进程更多的时间来清除它的写列表。这将造成LRU chain latch的竞争,因为服务器进程在你放弃与post一个free buffer waits事件之前搜索更多的free buffers。

.减小写批处理大小的阈值
这将强制数据库更频繁的flush写列表,增加在LRU chain的LRU端存在free buffer的可能性。为了减小写批处理大小,减小_db_large_dirty_queue参数的大小。如果数据库写进程正忙于写dirty list中的buffer到磁盘时,服务器进程将不能移动一个dirty buffer到写列表中。如果一个服务器进程正在寻找一个free buffer,并尝试移动一个不被频繁访问的dirty buffer到正执行写操作的dirty列表中,它将等待,并post一个free buffer waits等待事件。如果为了解决db file parallel write问题而增加了写批理处理大小,它可能增加的大多了。这不是很常见,但可能发生。

应用层面有两种解决方案:
.查找并优化物理IO语句
没有从磁盘读取数据块就不会出现free buffer waits等待事件。找到top物理IO SQL语句。通常只有少量大的SQL语句消耗物理IO很明显。通过优化或降低它们的执行频率来减少物理IO量。

.查找并减少DML SQL语句的影响。
因为free buffer waits等待事件与LRU chain中有太多的dirty buffers相关,这就肯定存在DML SQL语句。DMLSQL可能很难被找到因为它可能是一个高物理IO,高逻辑IO,高执行频率或高CPU消耗的语句。它可能是很多统计信息的巧妙组合。如果不能查看SQL的类型,那么查看top物理IO与逻辑IO SQL语句,然后检查语句本身。很有可能DML SQL也是top物理IO SQL语句。如果是这样,你就已经找到了关键SQL语句。

发表评论

电子邮件地址不会被公开。