Oracle Free Buffer Waits

eric0435發表於2019-04-12

等待事件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語句。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26015009/viewspace-2641204/,如需轉載,請註明出處,否則將追究法律責任。

相關文章