您好,登錄后才能下訂單哦!
Buffer Busy Waits是Oracle 數(shù)據(jù)庫常見的一個等待,特別是在并發(fā)寫比較頻繁的環(huán)境里。作為一個Oracle DBA,如果你從未遇到過Buffer Busy Waits等待,那么你算不上一個真正的Oracle DBA。
產(chǎn)生這個等待的原因是,在內(nèi)存級別,在同一時刻對同一內(nèi)存塊進行讀寫產(chǎn)生了爭用。這里我以2個進程對數(shù)據(jù)塊的操作為例,歸納出產(chǎn)生Buffer Busy Waits的場景:
寫寫,例如一個進程正在對內(nèi)存塊做更改,此時另一個進程也要修改這個內(nèi)存塊
寫讀,例如一個進程正在對內(nèi)存塊做更改,另一個進程此時想要讀這個內(nèi)存塊。你可能會問,oracle為什么不去copy正在修改的塊構造CR塊,然后去讀構造出的CR塊,理由很簡單,內(nèi)存塊正在寫(變化)的過程中去做copy,是一個不安全的動作。
按照字符的排列組合,還?!弊x讀“、”讀寫“兩個場景,這兩個場景都不會導致產(chǎn)生Buffer Busy Waits(這里很重要的一個假設是2個進程),理由我們后面再給出,這里按住不表。
Oracle為什么要設計這個等待,這是個好問題,本篇文章也主要是為了解答這個疑問。
“為什么”要比“是這樣做的”的更重要,前者講的是邏輯和洞見,后者描述的是過程和結果。
回答這個問題之前,首先設計一個場景,“假如沒有這個等待,讀寫數(shù)據(jù)塊是如何進行的”,當然這個場景是設計出來的,其實并不存在。
那我們開始,假設場景是這樣子的,我們要對一個數(shù)據(jù)塊做寫入操作,遵循如下步驟:
首先依據(jù)數(shù)據(jù)塊地址計算出(Hash算法)該塊所在的Hash Bucket。
根據(jù)桶的編號,計算出保護這個桶的CBC Latch,然后申請CBC Latch,查找數(shù)據(jù)塊在不在桶里,這里假設在內(nèi)存里。
修改數(shù)據(jù)塊。
釋放CBC Latch。
以上的描述看似是非常通暢,但是存在一個問題,由于CBC Latch的持有是排他的,在持有CBC Latch的情況下,去修改數(shù)據(jù)塊,那么這個Latch的持有時間就會比較長,這個長是相對于Latch的獲取和釋放這種CPU原子操作的,因此在持有CBC Latch的情況下修改數(shù)據(jù)塊,對于讀寫頻繁的數(shù)據(jù)庫/塊(熱點塊),那么勢必會造成CBC Latch的爭用,基于Latch的特性(自旋、休眠、再自旋),會造成大量CPU資源的浪費,導致數(shù)據(jù)庫的性能低下。
為了解決這個問題,Oracle設計了Buffer Pin的功能。也就是說,Buffer Pin這個機制存在的價值,是為了降低CBC Latch的爭用,節(jié)省CPU資源。
引入Buffer Pin后,修改數(shù)據(jù)塊的大致步驟如下:
首先依據(jù)數(shù)據(jù)塊地址計算出(Hash算法)該塊所在的Hash Bucket。
然后申請CBC Latch,查找、定位到數(shù)據(jù)塊
以X模式獲取數(shù)據(jù)塊的Buffer Pin。
釋放CBC Latch
在Pin的保護下,修改數(shù)據(jù)塊,完成后繼續(xù)以下步驟
獲得CBC Latch
釋放Buffer Pin
釋放CBC Latch
步驟復雜了許多,CBC Latch獲取/釋放共發(fā)生了兩次,工作量似乎整整大了兩倍。
可是極大程度降低了CBC latch的爭用。 因為持有CBC Latch的時間變得極短。 持有CBC Latch,只是為了在buffer header上增加buffer pin,目的變得單純和簡單。
你可能會說,Oracle這種解決方案就是按了葫蘆起了瓢,它只不過是轉移了競爭,以前是CBC Latch的爭用,現(xiàn)在是Buffer Pin的爭用。
這句話并沒錯。
但是Buffer Pin的爭用是不消耗CPU資源的。類似于隊列鎖的通知機制。而不會像Latch一樣去做自旋。
讀取數(shù)據(jù)塊增加S模式的Buffer Pin,修改數(shù)據(jù)塊增加X模式的Buffer Pin。S和S模式的Pin是兼容的,可以并發(fā)的讀取,X和S模式是不兼容的,后來的讀取會話需要產(chǎn)生等待。
文章的開頭,我們舉了2個進程操作同一內(nèi)存塊的例子,這里我們在有了一些知識之后,再做下總結和回顧。
寫讀,同一個時刻,如果一個進程以X模式持有了數(shù)據(jù)塊的Buffer Pin,另一個進程想以S模式持有,那么就會出現(xiàn)爭用,因為道理很簡單,X模式的Buffer Pin和S模式的Buffer Pin不兼容。
寫寫,同理,兩個同時欲修改同一個數(shù)據(jù)塊的進程,只有一個進程可以獲取X模式Buffer Pin,另一個會話產(chǎn)生Buffer Busy Waits等待。
我們平時經(jīng)常說讀不阻塞寫,寫不阻塞讀,那是在物理的數(shù)據(jù)塊級別,在內(nèi)存里,在同一時刻對于同一個內(nèi)存塊的寫讀/寫寫都是互相阻塞的。
這里接著上面把場景補充完整,“讀讀”不會產(chǎn)生Buffer Busy Waits等待,因為Pin模式是兼容的?!白x寫”不會產(chǎn)生Buffer Busy Waits等待,一個進程正在讀數(shù)據(jù)塊,此時另一個進程要去寫這個數(shù)據(jù)塊,寫進程很聰明,它會copy出一個current塊出來(之前的塊成為CR塊),然后在current塊上進行寫操作,通過這種方式避免了競爭,規(guī)避了Buffer Busy Waits的爭用。
因為前一個進程是在讀取,而不是修改。這個場景下的copy就是安全的。
另外一點,因為讀取數(shù)據(jù)庫塊需要在Buffer header上增加S模式的Buffer Pin,屬于內(nèi)存的修改操作,因此即使是讀取操作,CBC Latch的持有也是排他的,但是這個增加Pin的時間極短,在壓力正常的數(shù)據(jù)庫環(huán)境中引起CBC Latch大量爭用的可能性不大。
當然如果是大量進程對同一內(nèi)存塊頻繁讀取,就會引起CBC Latch的爭用。
最后,有必要對一些細節(jié)再做些補充:
一旦進程Pin住了一個數(shù)據(jù)塊,不需要立即去UNPin(移除Pin)。ORACLE認為在本次調(diào)用后還有可能繼續(xù)去訪問這個數(shù)據(jù)塊,因此直到本次調(diào)用結束才會做UNPin操作。
Oracle在對唯一索引/undo塊/唯一索引的回表/索引root、branch塊的設計上,在訪問(讀?。┑臅r候,獲取的是共享的CBC Latch,不需要去對Buffer加Pin,直接在持有共享CBC Latch的情況下讀取數(shù)據(jù)塊。這樣做的原因是這些類型的塊,發(fā)生修改的可能性比較小,因此Oracle單獨的采用這種機制。因此對于普通數(shù)據(jù)塊的讀取都是需要獲取2次CBC Latch,而對于這種特殊的數(shù)據(jù)塊,只獲取一次共享CBC Latch就可以了。
我們上面所說的情況都是在數(shù)據(jù)塊已經(jīng)存在在內(nèi)存里的情況。如果數(shù)據(jù)塊不在內(nèi)存,有可能會產(chǎn)生Read By Other Session爭用等待。
上面描述只符合10G后的版本。在10G前讀讀也會產(chǎn)生Buffer Busy Waits,10G后把這方面的Buffer Busy Waits歸到了Read By Other Session等待里。
本文講述了Buffer Busy Waits是如何產(chǎn)生的及其機制,但是并沒有講解如何對其進行調(diào)優(yōu)。Buffer Busy Waits等待設計的本質是為了降低CBC Latch的爭用。以兩個進程操作內(nèi)存塊為例,在“寫寫”、“寫讀”場景下會產(chǎn)生Buffer Busy Waits,在“讀讀”場景下不會產(chǎn)生Buffer Busy Waits等待,在“讀/寫”場景下,發(fā)生寫操作的服務器進程會去Copy出一個current塊來繼續(xù)寫操作,而不會去等待Buffer Busy Waits。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。