buffer cache深度分析及效能調整(六)

hanson發表於2019-05-11

4.3.2 buffer busy waits等待

當一個session在讀取或修改buffer cache裡的記憶體資料塊時,首先必須獲得cache buffers chains latch,獲得以後,到hash chain上遍歷直到找到需要的buffer header後。這時,該session必須在該buffer header上以shareexclusive模式(具體哪個模式由該session的操作決定)獲得一個buffer lock或一個buffer pin。一旦buffer headerpin住,session就將釋放cache buffers chains latch,然後可以在該buffer上進行操作了。如果無法獲得buffer pin,那麼該session就會等待buffer busy waits等待事件。該等待事件不會出現在session的私有PGA裡。

buffer busy waits等待事件不能像latch free等待那樣可以相對比較容易的進行事後跟蹤。對於該等待事件,oracle提供了v$waitstat檢視。v$waitstat裡的記錄都是buffer busy waits等待事件發生時進行更新的。也就是說,該檢視體現的都是buffer busy waits等待事件的統計資料。但這隻能給你提供一個大概的buffer busy waits的分佈。如果要想具體的診斷該等待事件,只能當發生該等待時,到v$session_wait裡去找原因,從而才能找到解決的辦法。處理buffer busy wait等待事件時,首先使用下面的SQL語句找到發生等待的資料塊類別以及對應的segment

select 'Segment Header' class,

        a.segment_type, a.segment_name, a.partition_name

from   dba_segments a, v$session_wait b

where  a.header_file  = b.p1

and    a.header_block = b.p2

and    b.event        = 'buffer busy waits'

union

select 'Freelist Groups' class,

        a.segment_type, a.segment_name, a.partition_name

from   dba_segments a, v$session_wait b

where  b.p2 between a.header_block + 1 and (a.header_block + a.freelist_groups)

and    a.header_file     = b.p1

and    a.freelist_groups > 1

and    b.event           = 'buffer busy waits'

union

select a.segment_type || ' block' class,

       a.segment_type, a.segment_name, a.partition_name

from   dba_extents a, v$session_wait b

where  b.p2 between a.block_id and a.block_id + a.blocks - 1

and    a.file_id  = b.p1

and    b.event    = 'buffer busy waits'

and    not exists (select 1

                   from   dba_segments

                   where  header_file  = b.p1

                   and    header_block = b.p2);

              然後,根據不同的資料塊型別進行相應的處理。

1)     如果資料塊型別為data block,如果版本為10g之前,則可以同時參照p3列的值來共同診斷。如果p3130意味著同時有很多session在訪問同一個data block,而且該data block沒有在記憶體裡,而必須從磁碟上獲取。有三種方法可以降低該事件出現的頻率:

a、降低併發性。這個比較難實現。

b、找出並優化含有這些segmentSQL語句,以降低物理和邏輯讀。

c、增加freelistsfreelist groups

如果沒有足夠的freelists,當同時對同一個表進行insert時,這就很容易引起buffer busy waits等待。如果正在等待buffer busy waitssession正在進行insert操作,那麼需要檢查以下那個表有多少freelists了。當然,由於freelists的不足主要會導致對於segment headerbuffer busy waits等待。

如果p3220意味著有多個session同時修改在一個block(block已經被讀入記憶體了)裡的不同的行。這種情況通常出現在高DML併發性的環境裡。有三種方法可以降低該事件出現的頻率:

a、降低併發性。這個比較難實現。

b、通過增加pctfree減少block裡含有的行數。

c、將該物件移到擁有較小block尺寸的表空間裡(9i或以上)

2)     如果資料塊型別為data segment header(表或索引的segment header,不是undo segment header)上發生buffer busy waits等待事件,通常表明資料庫裡有些表或索引的段頭具有頻繁的活動。

程式訪問segment header主要有兩種原因:一是獲得或修改process freelists資訊;二是擴充套件HWM。有三種方法可以降低該事件出現的頻率:

a、增加爭用物件的freelistsfreelist groups的數量。

b、確定pctfreepctused之間的間隔不要太小。

c、確保next extent的尺寸不要太小。

d9i以後,使用ASSM特性來管理block

3)     如果資料塊型別為undo segment headers的爭用等待,表明資料庫中的rollback segments太少,或者他們的extent size太小,導致對於同一個segment header的大量更新。如果使用了9i以後的auto undo management,則不用處理,因為oracle會根據需要自動建立新的undo segments。如果是9i之前,則可以建立新的private rollback segments,並把它們online,或者通過降低transactions_per_rollback_segment引數來減輕該等待。

4)     如果資料塊型別為undo block,說明有多個session同時訪問那些被更新過的block。這是應用系統的問題,在資料庫來說對此無能為力。

4.3.3 free buffer waits等待

在一個資料塊被讀入buffer cache之前,oracle程式必須為該資料塊獲得一個對應的可用的記憶體數

據塊。當sessionLRU list上無法發現一個可用的記憶體資料塊或者搜尋可用的記憶體資料塊被暫停的時候,該session就必須等待free buffer waits事件。

從前面的描述,我們已經知道,一個需要可用記憶體資料塊的前臺程式會連續掃描LRU 連結串列,直到達到一個限定值(也就是隱藏引數_db_block_max_scan_pct所指定的值,表示已經掃描的buffer header數量佔整個LRU連結串列上的buffer header的總數量,在9i中該限定值為40%)。如果到該限定值時還沒找到可用記憶體資料塊時,該前臺程式就會觸發DBWR程式以便清空一些髒資料塊,從而使得在輔助LRU連結串列上能夠掛上一些可用的記憶體資料塊。在DBWR程式工作時,該前臺程式就必須等待free buffer waits

oracle跟蹤每次對於可用的記憶體資料塊的請求次數(記錄在v$sysstat裡的free buffer requested),也跟蹤每次請求可用的記憶體資料塊失敗的次數(記錄在v$system_event裡的free buffer waitstotal_waits)。而v$sysstat裡的free buffer inspected則說明oracle為了找到可用的記憶體資料塊所所跳過的資料塊的個數,如果buffer cache很空,有很多空的資料塊的話,則該值為0。如果free buffer inspected相對free buffer requested來說很高,則說明oracle程式需要掃描更多的LRU連結串列上的資料塊才可以找到可用的資料塊。

SQL> select *

  2  from   v$sysstat

  3  where  name in ('free buffer requested', 'free buffer inspected');

STATISTIC# NAME                                   CLASS      VALUE

---------- ------------------------------   ----------- ----------

        75 free buffer requested                 8            290532493

        79 free buffer inspected                 8               2983596

SQL> select *

  2  from   v$system_event

  3  where event = 'free buffer waits';

EVENT             TOTAL_WAITS TOTAL_TIMEOUTS TIME_WAITED AVERAGE_WAIT TIME_WAITED_MICRO

----------------- ----------- -------------- ----------- ------------ -----------------

free buffer waits        1003            476       71075           71         710749256

可以看到,該系統的free buffer waits等待很少,總共等待的時間才0.476秒。同時也可以看到,請求了290532493free buffer requested個可用的記憶體資料塊,但是在這個過程中只跳過了2983596free buffer inspected)個資料塊,二者相差2個數量級。說明系統很容易就找到可用的記憶體資料塊。

如果一個session花費了很多的時間等待free buffer waits等待事件的話,通常可能有以下原因:

1)     低效率的SQL語句:對於那些引起很大邏輯讀的SQL語句(v$sql裡的disk_reads),那些SQL語句可能進行了全表掃描,索引全掃描、或者通過了不正確的索引掃描表等。調整這些SQL語句以降低邏輯讀。

2)     DBWR程式不夠多:也可以通過增加DBWR checkpoints的個數來降低free buffer waits9i下,可以通過減小fast_start_mttr_target引數來縮短MTTR,從而增加DBWR程式啟動的次數。然而,這也有可能引起程式等待write complete waits事件。

3)     I/O子系統太慢。

4)     延遲的塊清除(block clearouts):通常發生的情形是,晚上向資料庫匯入了一個很大的表。然後早上執行應用系統時,會發現有有程式在等待buffer busy waits。這是因為第一個訪問該表的程式將進行一個延遲的塊清除,而這會導致free buffer waits等待事件。解決方法是在匯入表完畢以後,執行一句全表掃描,比如通常是:select count(*) from該大表。這樣在後面的程式再次訪問的時候就不會產生free buffer waits等待事件了。

5)     buffer cache太小:遇到free buffer waits事件,首先想到的就是增加buffer cache的大小。

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

相關文章