Oracle为什么写阻塞读导致Buffer Busy Waits
这之前需要了解在内存中定位并读取一个buffer的流程,先看看为什么读不阻塞写。
读不阻塞写:
假设会话s1在读取db buffer cache中读取需要的buffer过程中,会获取CBC Latch后查找定位buffer后,以共享S模式获取BH(Buffer Header)的Buffer Pin(以下简称BP锁)。
此时,如果会话s2需要修改同样的buffer的话,在发现BH上S模式的BP锁后,由于S模式的BP锁是互相兼容的,于是s2在BH上同样留下一个S模式的BP锁,然后在共享S模式BP锁下通过BH访问实际buffer后,将buffer直接copy一个新的buffer出来。
而原来的buffer变为CR块,新的buffer则是XCUR状态,s2则在新的buffer上进行写操作,避免了Buffer Busy Waits的争用。因为s1是在读取,这个场景下的copy就是安全的。
写阻塞读:
那么反过来,如果s1会buffer的BH以独占的X模式进行写操作,此时s2想要读这个buffer的BH加S模式的BP锁,但是发现了已经有X模式的SP锁了,由于S和X模式互不兼容,那么s2进程进入等待,等待事件就是Buffer Busy Waits。
几年前看吕海波的《Oracle内核技术揭秘》的时候,在读到Buffer Busy Waits产生原理的时候,曾经有过这样的疑问:
1.为什么不构造新的CR块
2.为什么不去读已有的CR块
对于第一个问题,前边说过读不阻塞写的时候因为s1是在读取那么copy就是安全的。反过来写阻塞读,s1是在修改,你无法判断buffer是否已经被修改那么copy则不安全。
当然你也可以想,无非就修改和未修改两种状态,Oracle可不可以做个判断,
如果是未修改那么和读不阻塞写一样你直接copy就行,
如果是已经修改了那么也copy一样的buffer然后将copy后的buffer做CR还原。
其实有点想当然了,首先想要实现能不能判断是否修改这个操作需要花费的消耗就不好预估,因为修改过程就可以分成好几个步骤(比如涉及undo和redo等),这几个步骤完成算修改完那么其实BP锁都释放了也有可能。总不能是其中某个步骤吧?
其次所谓的CR还原是需要undo中的修改前的数据的,进行CR还原还要读取undo块消耗更大。
更何况既然s1的X模式BP锁没释放说明写操作正在进行,undo数据是否生成可用还不好说。
因此,修改状态下X模式的BP锁写copy是不安全的。
https://www.cnblogs.com/PiscesCanon/p/17685492.html
而第二个问题,那么更不现实。CR块是有版本的,哪个会话构造的CR块就哪个会话用。
假设某个被block修改很频繁,t1,t2,t3,t4,t5时刻的值各不相同。
所谓的CR块即可能是t1时刻的映像,也可能是t4时刻的,也可能同时存在同个block块的多个CR块,你用哪个CR?该进行怎样的判断?想想就特别麻烦且不可靠。
因此CR块是不共享的。