本文中根块,枝块,叶块,表块分别是索引根块,索引枝块,索引叶块,数据表块的简称。
此外本文大多数观点来自于大师吕海波《Oracle内核技术揭秘》一书,本博文为个人感想。
首先需要明确4点关于CBC LATCH和BUFFER PIN的知识点:
1. 对于根块和枝块,CBC LATCH都是以S模式获取的,无BUFFER PIN
2. 对于叶块,CBC LATCH是以X模式获取的,BUFFER PIN是以S模式获取的
3. 对于表块,CBC LATCH是以X模式获取的,SELECT语句的BUFFER PIN是以S模式获取的,DML语句的BUFFER PIN是以X模式获取的
4. 以上三点基本正确,但有一个特殊情况,INDEX UNIQUE SCAN的情况下是这样的:
1)对于根块、枝块、叶块,CBC LATCH都是以S模式获取的,且都无BUFFER PIN
2)对于表块,SELECT语句的CBC LATCH是以S模式获取的,无BUFFER PIN;DML语句的CBC LATCH是以X模式获取的,BUFFER PIN是以X模式获取的
现在我们考虑一种最复杂的情况:不走索引唯一扫描但走索引的情况。
此时将CBC LATCH和BUFFER PIN的获取方式进行分类总结:
A 根块和枝块:以S模式获取CBC LATCH-->>读取索引数据-->>释放CBC LATCH
B 叶块:以X模式获取CBC LATCH-->>以S模式获取BUFFER PIN,释放CBC LATCH,读取索引数据-->>以X模式获取CBC LATCH,释放BUFFER PIN-->>释放CBC LATCH
C 表块(SELECT):以X模式获取CBC LATCH-->>以S模式获取BUFFER PIN,释放CBC LATCH,读取数据-->>以X模式获取CBC LATCH,释放BUFFER PIN-->>释放CBC LATCH
D 表块(DML):以X模式获取CBC LATCH-->>以X模式获取BUFFER PIN,释放CBC LATCH,修改数据-->>以X模式获取CBC LATCH,释放BUFFER PIN-->>释放CBC LATCH
那么对于SELECT和DML语句我们就可以使用以上组合来显示出其获取内部锁和闩的过程:
1.SELECT语句的流程是:ABC
2.DML语句的流程是:ABD
现在我们模拟所有情况下的内部闩锁争用:(我们假设最差的情况,SELECT和DML语句都走同样的索引,查同样的数据)
一:SELECT语句的并发,即ABC并发ABC
A阶段全是S模式不会有争用发生,BC阶段获取X模式CBC LATCH的时间极短(只为修改BUFFER PIN的状态),BC阶段的BUFFER PIN是S模式,也不会引发争用。(10G之前会)
因此SELECT并发不会引发BUFFER BUSY WAITS
二:SELECT语句和DML语句的并发,即ABC并发ABD
A阶段全是S模式不会有争用发生,B阶段获取X模式CBC LATCH的时间极短(只为修改BUFFER PIN的状态),也不会发生争用,那么C和D阶段:
如果C在前那么D貌似会发生等待,但其实会进行构造CR块的操作,并在独占CBC LATCH的保护下将当前BUFFER HEADER(简称BH)修改为CR块,克隆出来的块的BH修改为XCUR的当前块,不会发生等待。但是如果构造完CR块完此时又有一个C或D并发过来,由于克隆块是XCUR块上边被加了X模式的BUFFER PIN,那么就会发生等待,这里就是BUFFER BUSY WAITS啦。
如果D在前,那么同样的BUFFER BUSY WAITS出现,所以说只要在D之后并发了C或者D,都会造成BUFFER BUSY WAITS。
之前在此处有些疑问,为何后来的C并发不会发生一致性读呢,后来想想D已经加了X模式的BUFFER PIN,一致性读是需要读取表块的ITL槽信息的,这里C连块的BUFFER PIN都获取不到,谈何一致性读。
真正的一致性读应当指涉及UNDO的操作,因为既然DML发现了BUFFER PIN上的S模式的锁证明这个块正在被读,根本无需一致性读。
三:DML语句的并发
这里就很显然了,ABD的AB阶段不会发生明显争用,D阶段一个会话获取了X模式的BUFFER PIN之后,另一个必然要等待,而且此时如果再来一个SELECT语句,也会被阻塞,按照事务锁的机制,SELECT是不会被DML阻塞的,但是这里并不是事务锁阻塞而是BUFFER PIN的等待。
总结:
我们知道在数据库中无论是读取还是修改一个块都是极其迅速的,因此即便是以上的并发情况下,由于每个块的BUFFER PIN锁持有时间都极短,因此单个块即便发生BUFFER BUSY WAITS时间也基本可以忽略不计。
除非更改大量的块导致多个块发生BUFFER BUSY WAITS等待,因此如果发生了BUFFER BUSY WAITS,只和DML有关,由于DML也会导致索引更新,因此出现BUFFER BUSY WAITS尽量优化DML语句。
而如果发生了CBC LATCH的等待,那就更明显了,一定是并发过多,甚至和DML无关,只要是个SQL并发太多都会导致CBC LATCH的等待,因为表块和叶块都是以独占模式获取的CBC LATCH,解决办法要么是减小并发,要么全部优化为走INDEX UNIQUE SCAN,或者通过修改隐含参数_db_block_hash_latches和_db_block_hash_buckets增大CBC LATCH和HASH BUCKET的数量。
但是需要明确的是:
BUFFER BUSY WAITS和CBC LATCH的等待其本质都是并发量过大引起,并不推荐以上使用更改隐含参数的方式解决CBC LATCH的争用。对于BUFFER PIN的争用DML语句的优化空间也很小,只能尝试创建唯一索引,因此应当考虑从业务角度降低并发。