buffer cache深度分析之buffer cache的優化

1 buffer cache的設置優化

   buffer cache的設置隨着oracle版本的升級而不斷變化。8i下使用db_block_buffers來設置,該參數表示buffer cache中所能夠包含的內存數據塊的個數;9i以後使用db_cache_size來設置,該參數表示buffer cache的總共的容量,可以用字節、K、M爲單位來進行設置。而到了10g以後則更加簡單,甚至可以不用去單獨設置buffer cache的大小。因爲10g引入了ASMM(Automatic Shared Memory Management)這樣一個可以進行自我調整的組件,該組件可以自動調整shared pool size、db cache size等SGA中的組件。只需要設置sga_target參數,則其他組件就能夠根據系統的負載和歷史信息自動的調整各個部分的大小。要啓動ASMM,只需要設置statistics_level爲typical或all。

   oracle8.0以前只能設置一種buffer cache,而從8.0以後,oracle提供了三種類型的buffer cache,分別是default、keep、recyle。keep和recycle是可選的,default必須存在。8i以前使用db_block_buffer設置default、buffer_pool_keep設置keep、buffer_pool_recycle設置recyle。而8i以後使用db_cache_size設置default、db_keep_cache_size設置keep、db_recycle_cache_size設置recycle。10g不能自動設置db_keep_cache_size和db_recycle_cache_size,必須手工設置。
同時,8i以前,這三種buffer cache是獨立指定的,互不制約。而8i以後,這三種buffer cache是有相互制約關係的。如果指定了keep和recycle的buffer cache,則default類型的buffer cache的大小就是db_cache_size - buffer_pool_keep - buffer_pool_recycle。

    通常將經常訪問的對象放入keep類型的buffer cache裏,而將不常訪問的大表放入recycle類型的buffer cache裏。其他沒有指定buffer cache類型的對象都將進入default類型的buffer cache裏。爲對象指定buffer cache類型的方法如下:
SQL> create table test (n number) storage (buffer_pool keep); SQL> alter table test storage (buffer_pool recycle);
  如果沒有指定buffer_pool短語,則表示該對象進入default類型的buffer cache。

  這裏要說明的是,從名字上看,很容易讓人誤以爲這三種buffer cache提供了三種不同的管理內存數據塊的機制。但事實上,它們之間在管理和內部機制上沒有任何的區別。它們僅僅是爲DBA們提供了一個選擇,就是能夠將數據庫對象分成“非常熱的”、“比較熱的”和“不熱的”這三種類型。因爲數據庫中總會存在一些“非常熱”的對象,它們頻繁的被訪問。而如果某個時候系統偶爾做了一次大表的全表掃描,就有可能將這些對象清除出內存。爲了防止這種情況的發生,我們可以設置keep類型的buffer cache,並將這種對象都移入keep buffer cache中。同樣的,數據庫中也總會有一些很大的表,可能每天爲了生成一張報表,而只需要訪問一次就可以了。但有可能就是這麼一次訪問,就將大部分的內存數據塊清除出了buffer cache。爲了避免這種情況的發生,可以設置recycle類型的buffer cache,並將這種偶爾訪問的大表移入recycle buffer cache。

  毫無疑問,如果你要設置這三種類型的buffer cache,你需要自己研究並等於你的數據庫中的對象進行分類,並計算這些對象的大小,從而才能夠正確的把它們放入不同的buffer cache。但是,不管怎麼說,設置這三種類型的buffer cache只能算是最低層次的優化,也就是說在你沒有任何辦法的情況下,可以考慮設置他們。但是如果你能夠優化某條buffer gets非常高SQL使其buffer gets降低50%的話,就已經比設置多個buffer cache要好很多了。

    9i以後還提供了可以設置多種數據塊尺寸(2、4、8、16 或 32k)的buffer cache,以便存放不同數據塊尺寸的表空間中的對象。使用初始化參數:db_Nk_cache_size來指定不同數據塊尺寸的buffer cache,這裏的N就是2、4、8、16 或 32。創建數據庫時,使用初始化參數:db_block_size所指定缺省的數據塊尺寸用於system表空間。然後可以指定最多4個不同數據塊尺寸的表空間,每種數據塊尺寸的表空間必須對應一種不同尺寸的buffer cache,否則不能創建不同數據塊尺寸的表空間。
SQL> create tablespace tbs_test_16k 2 datafile 'C:\oracle\oradata\ora92\tbs_test_16k.dbf' size 10M 3 blocksize 16k; create tablespace tbs_test_16k * ERROR 位於第 1 行: ORA-29339: 表空間塊大小 16384 與配置的塊大小不匹配 SQL> show parameter db_16k_cache_size NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ db_16k_cache_size big integer 0
  我們可以看到,由於16k數據塊所對應的buffer cache沒有指定,所以創建16k數據塊的表空間會失
敗。於是我們先設置db_16k_cache_size,然後再試着創建16k數據塊的表空間。
SQL> alter system set db_16k_cache_size=10M; 系統已更改。 SQL> create tablespace tbs_test_16k 2 datafile 'C:\oracle\oradata\ora92\tbs_test_16k.dbf' size 10M 3 blocksize 16k; 表空間已創建。
  不同尺寸數據塊的buffer cache的管理和內部機制與缺省數據塊的buffer cache沒有任何的分別。它最大的好處是,當使用可傳輸的表空間從其他數據庫中將不同於當前缺省數據塊尺寸的表空間傳輸過來的時候,可以不做很多處理的直接導入到當前數據庫,只需要設置對應的數據塊尺寸的buffer cache即可。同時,它對於調優OLTP和OLAP混合的數據庫也有一定的用處。OLTP環境下,傾向於使用較小的數據塊,而OLAP環境下,由於基本都是執行全表掃描,因此傾向於使用較大的數據塊。這時,可以將OLAP的錶轉移到使用大數據塊(比如32k)的表空間裏去。而將OLTP的表放在中等大小的數據塊(比如8k)的表空間裏。
對於應該設置buffer cache爲多大,oracle從9i開始通過設置初始化參數:db_cache_advice,從而提供了可以參照的建議值。oracle會監控default類型、keep類型和recycle類型的buffer cache的使用,以及其他五種不同數據庫尺寸(2、4、8、16 或 32k)的buffer cache的使用。在典型負荷的時候,啓用該參數,從而收集數據幫助用戶確定最佳的db_cache_size的大小。該參數有三個值:
   1) off:不收集數據。
   2) on:開始分配內存收集數據,有可能引發CPU和內存的負擔,可能引起4031錯。
   3) ready:不收集數據,但是收集數據的內存已經預先分配好了。通過把該參數值從off設置爲ready,然後再設置爲on,就可以避免出現4031錯。

    oracle會根據當前所監控到的物理讀的速率,從而估算出在不同大小尺寸的buffer cache下,所產生的可能的物理讀的數量。oracle會將這些收集到的信息放入視圖:v$db_cache_advice中。每種類型的buffer cache都會有相應的若干條記錄來表示所建議的buffer cache的大小。比如下面,我們顯示對於缺省類型的、缺省數據塊尺寸的buffer cache的建議大小應該是多少。
SQL> SELECT size_for_estimate, buffers_for_estimate, 2 estd_physical_read_factor,estd_physical_reads 3 FROM v$db_cache_advice 4 WHERE NAME = 'DEFAULT' 5 AND block_size = (SELECT VALUE 6 FROM v$parameter 7 WHERE NAME = 'db_block_size') 8 / SIZE_FOR_ESTIMATE BUFFERS_FOR_ESTIMATE ESTD_PHYSICAL_READ_FACTOR ESTD_PHYSICAL_READS ----------------- -------------------- ------------------------- ------------------- 4 500 1.3869 40154 8 1000 1.3848 40093 12 1500 1.1861 34339 16 2000 1.1397 32996 20 2500 1 28952 24 3000 1 28952 28 3500 1 28952 32 4000 1 28952 36 4500 0.8671 25104 40 5000 0.8671 25104 44 5500 0.8671 25104 48 6000 0.7422 21488 52 6500 0.7422 21488 56 7000 0.7422 21488 60 7500 0.554 16040 64 8000 0.554 16040 68 8500 0.554 16040 72 9000 0.554 16040 76 9500 0.554 16040 80 10000 0.554 16040
    這裏的字段estd_physical_read_factor表示在相應的buffer cache尺寸(由字段size_for_estimate表示)
 下,估計從硬盤裏讀取數據的次數除以在內存裏讀取數據的次數。如果沒有發生物理讀則該比值爲空。在
 內存足夠的前提下,這個比值應該是越低越好的。從上面的輸出中,我們可以看到,如果將buffer cache
設置爲60M,可以獲得較好的性能,物理讀也將會有一個顯著的下降。但是設置爲大於60M的話(比如
64M或68M),則不會降低物理讀,反而浪費內存空間。所以從上面的查詢結果中,我們可以知道,設置
爲60M是比較合適的。
2 buffer cache的統計信息

   爲了對buffer cache進行性能的診斷,oracle提供了很多有關buffer cache的統計信息。這些統計信息大致可以分成三類:1)有關用戶發出的對內存數據塊的請求相關的統計信息;2)有關DBWR後臺進程對內存數據塊處理相關的統計信息;3)RAC相關的統計信息。

      我們在診斷buffer cache時,不需要關注所有的統計信息。這裏主要介紹幾個重要的統計信息,其他的統計信息都可以到《Oracle9i Database Reference: Appendix C》中找到。如下所示:

SQL> SELECT name, value FROM v$sysstat WHERE name in ( 2 'session logical reads', 3 'physical reads', 4 'physical reads direct', 5 'physical reads direct (lob) ', 6 'consistent gets', 7 'db block gets', 8 'free buffer inspected') 9 / NAME VALUE ---------------------------------------------------------------- ---------- session logical reads 73797 db block gets 498 consistent gets 73299 physical reads 29017 free buffer inspected 0 physical reads direct 40
  這裏做些簡單的解釋。
     1) session logical reads:所有的邏輯讀的數據塊的數量。注意,其中包括先從硬盤上讀數據塊到內存裏,再從內存裏讀數據塊。
    2) consistent gets:在一致性(consistent read)讀模式下讀取的內存裏的數據塊數量。包括從rollback segment裏讀取的數據塊數量以及從data block buffer裏讀取的數據塊數量。主要是通過select產生的。Update/delete也能產生很少量的此類數據塊。注意:如果oracle的運行時間過長,由於oracle的bug導致consistent gets大大超過實際的數量。因此建議使用‘no work - consistent read gets’, ‘cleanouts only - consistent read gets’,‘rollbacks only - consistent read gets’, ‘cleanouts and rollbacks - consistent read gets’之和來代替consistent gets的值。
    3) db block gets:在當前(current)模式下讀取的內存裏的數據塊的數量。不是讀取過去某個時點的數據塊,而必須是當前最新的數據塊。主要是通過update/delete/insert來產生的,因爲DML需要當前最新的數據塊才能對之進行改變。在字典管理表空間下,一些獲得當前可用擴展空間的select語句也會產生此類數據塊,因爲必須得到當前最新的空間使用信息才能擴展。邏輯上,session logical reads = consistent gets + db block gets。
    4) physical reads:從硬盤裏讀取的數據塊的數量。注意,這個數量大於實際從硬盤裏讀取的數量,因爲這部分block也包括了從操作系統緩存裏讀取的數據塊數量。
    5) physical reads direct:有些數據塊不會先從硬盤讀入內存再從內存讀入PGA再傳給用戶,而是繞過SGA直接從硬盤讀入PGA。比如並行查詢以及從臨時表空間讀取數據。這部分數據塊由於不緩存使得hit ratio不會被提高。
    6) physical reads direct (lob):與physical reads direct一樣。
    7) free buffer inspected:這個值表示爲了找到可用數據塊而跳過的數據塊的數量。這些被跳過的數據塊就是髒的或被鎖定的數據塊。明顯,這個值如果持續增長或很高,就需要增加buffer cache的大小了。

   在獲得了這些統計信息以後,我們可以計算buffer cache的命中率:
1Hit Ratio = 1 – (physical reads – physical reads direct - physical reads direct (lob) ) / session logical reads 2Miss ratio = (physical reads – physical reads direct- physical reads direct (lob) ) / session logical reads
 
   通常在OLTP下,hit ratio應該高於0.9。否則如果低於0.9則需要增加buffer cache的大小。在考慮
 調整buffer cache hit ratio時,需要注意以下幾點。
    1) 如果上次增加buffer cache的大小以後,沒有對提高hit ratio產生很大效果的話,不要盲目增加buffer cache的大小以提高性能。因爲對於排序操作或並行讀,oracle是繞過buffer cache進行的。
    2) 在調整buffer cache時,儘量避免增加很多的內存而只是提高少量hit ratio的情況出現。
我們還可以查詢每種buffer cache的統計信息,主要關注的還是consistent_gets和db_block_gets以及
 physical_reads的值。
SQL> SELECT name, block_size,physical_reads, db_block_gets,consistent_gets 2 FROM v$buffer_pool_statistics; NAME BLOCK_SIZE PHYSICAL_READS DB_BLOCK_GETS CONSISTENT_GETS -------------------- ---------- -------------- ------------- --------------- DEFAULT 8192 28978 719 77591 DEFAULT 16384 2 80 11
      v$sysstat中名稱以DBWR開頭的都是有關DBWR後臺進程相關的統計信息。當DBWR進程寫完髒數據塊以後或者掃描完LRU鏈表以後更新這些統計信息。DBWR會基於被觸發的頻率以及所處理的內存數據塊的數量與總內存數據塊的數量的比例,來進行自我調整。我們可以通過這些統計信息得到一些對當前DBWR運行情況的認識。
3 buffer cache的等待事件
    與buffer cache相關的等待事件包括:latch free、buffer busy waits、free buffer waits。曾經發生過的等
待事件可以從v$system_event(一個等待事件對應一行記錄)和v$session_event(一個session一個等待事件對應一行記錄)中看到。而當前系統正在經歷的等待事件可以從v$session_wait看到。

3.1 latch free等待

     等待事件“latch free”中與buffer cache有關的有兩類:cache buffers chains latch和cache buffers lru chain latch。在理解了上面所描述的有關buffer cache的內部管理機制以後,就應該很容易理解這兩個latch產生的原因。

    對於buffer cache中的每個hash chain鏈表來說,都會有一個名爲cache buffers chains latch的latch來保護對hash chain的併發操作,這種latch通常也叫作hash latch或CBC latch。數據庫中會有很多的cache buffers chains latch,每個latch都叫做child cache buffers chains latch。一個child cache buffers chains latch會管理多個hash chain。前面我們知道,hash chain的數量由一個隱藏參數:_db_block_hash_buckets決定。同樣也有一個隱藏參數:_db_block_hash_latches來決定有多少個cache buffers chains latch來管理這些hash chain。該參數的缺省值由buffer cache中所含有的內存數據塊的多少決定,當內存數據塊的數量
     •少於2052個時,_db_block_hash_latches = power(2,trunc(log(2, 內存塊數量 - 4) - 1))
    •多於131075個時,_db_block_hash_latches = power(2,trunc(log(2, db_block_buffers - 4) - 6))
    •位於2052與131075 buffers之間,_db_block_hash_latches = 1024
可以使用下面的SQL語句來確定當前系統的cache buffers chains latch的數量。
SQL> select count(distinct(hladdr)) from x$bh; COUNT(DISTINCT(HLADDR)) ----------------------- 1024 SQL> select count(*) from v$latch_children where name='cache buffers chains'; COUNT(*) ---------- 1024
     在知道了cache buffers chains latch的數量以後,我們只需要用hash chain的數量除以latch的數量以後,就可以算出每個latch管理多少個hash chain了。我們將下面7532除以1024,就可以知道,當前的系統中,每個latch大概對應8個hash chain。
SQL> select x.ksppinm, y.ksppstvl, x.ksppdesc 2 from x$ksppi x , x$ksppcv y 3 where x.indx = y.indx 4 and x.ksppinm like '\_%' escape '\' 5 and ksppinm like '%_db_block_hash_buckets%' 6 ; KSPPINM KSPPSTVL KSPPDESC ---------------------- -------- ------------------------------------- _db_block_hash_buckets 7523 Number of database block hash buckets
    當數據庫在hash chain搜索需要的數據塊時,必須先獲得cache buffers chains latch。然後在掃描hash chain的過程中會一直持有該latch,直到找到所要的數據塊纔會釋放該latch。當有進程一直在掃描某條hash chain,而其他進程也要掃描相同的hash chain時,其他進程就必須等待類型爲cache buffers chains latch的latch free等待事件。

    不夠優化的SQL語句是導致cache buffers chains latch的主要原因。如果SQL語句需要訪問過多的內存數據塊,那麼必然會持有latch很長時間。找出邏輯讀特別大的sql語句進行調整。v$sqlarea裏那些buffer_gets/executions爲較大值的SQL語句就是那些需要調整的SQL語句。這種方式不是很有針對性,比較盲目。網上曾經有人提供了一個比較有針對性的、查找這種引起較爲嚴重的cache buffers chains latch的SQL語句的方式,其原理是根據latch的地址,到x$bh中找對應的buffer header,x$bh的hladdr表示該buffer header所對應的latch地址。然後根據buffer header可以找到所對應的表的名稱。最後可以到v$sqltext(也可以到stats$sqltext)中找到引用了這些表的SQL語句。我也列在這裏。where條件中的rownum<10主要是爲了不要返回太多的行,只要能夠處理掉前10個latch等待就能有很大改觀。
select /*+ rule */ s.sql_text from x$bh a,dba_extents b, (select * from (select addr from v$latch_children where name = 'cache buffers chains' order by sleeps desc) where rownum<11) c, v$sqltext s where a.hladdr = c.addr and a.dbarfil = b.relative_fno and a.dbablk between b.block_id and b.block_id + b.blocks and s.sql_text like '%'||b.segment_name||'%' and b.segment_type='TABLE' order by s.hash_value,s.address,s.piece /
    還有一個原因可能會引起cache buffers chains latch,就是熱點數據塊問題。這是指多個session重複訪問一個或多個被同一個child cache buffers chains latch保護的內存數據塊。這主要是應用程序的問題。大多數情況下,單純增加child cache buffers chains latches的個數對提高性能沒有作用。這是因爲內存數據塊是根據數據塊地址以及hash chain的個數來進行hash運算從而得到具體的hash chain的,而不是根據child cache buffers chains latches的個數。如果數據塊的地址以及hash chain的個數保持一致,那麼熱點塊仍然很有可能會被hash到同一個child cache buffers chains latch上。可以通過v$session_wait的p1raw字段來判斷latch free等待事件是否是由於出現了熱點塊。如果p1raw保持一致,那麼說明session在等待同一個latch地址,系統存在熱點塊。當然也可以通過x$bh的tch來判斷是否出現了熱點塊,該值越高則數據塊越熱。
SQL> select sid, p1raw, p2, p3, seconds_in_wait, wait_time, state 2 from v$session_wait 3 where event = 'latch free' 4 order by p2, p1raw; SID P1RAW P2 P3 SECONDS_IN_WAIT WAIT_TIME STATE ---- -------- --- --- --------------- ---------- ------------------ 38 6666535C 13 1 1 2 WAITED KNOWN TIME 42 6666535C 13 1 1 2 WAITED KNOWN TIME 44 6666535C 13 3 1 4 WAITED KNOWN TIME ……………………… 85 6666535C 13 3 1 12 WAITED KNOWN TIME 214 6666535C 138 1 1 2 WAITED KNOWN TIME
接下來,我們就可以根據p1raw的值去找到所對應的內存數據塊以及對應的表的名稱了。
select a.hladdr, a.file#, a.dbablk, a.tch, a.obj, b.object_name from x$bh a, dba_objects b where (a.obj = b.object_id or a.obj = b.data_object_id) and a.hladdr = '6666535C';
要解決熱點塊的問題,可以通過將熱點塊中的行分散到多個數據塊中去,這樣原來的熱點塊就變成了多個數據塊,這樣被hash到同一個latch的機率就降低了。如果熱點塊屬於表,則可以先將表的數據導出來,然後增加表的pctfree值,最後將數據再導入。如果熱點塊屬於索引,則可以設定較高的 pctfree參數後,重建索引。注意,這會增加索引的高度。

    通過前面我們已經知道,每個working set都會有一個名爲cache buffers lru chain的latch(也叫做lru latch)來管理。任何要訪問working set的進程都必須先獲得cache buffers lru chain latch。cache buffers lru chain latch爭用也是由於低效的掃描過多的內存數據塊的SQL語句引起的。調整這些語句以降低邏輯讀和物理讀。只要修改一下上面找引起cache buffers chains latch的SQL語句即可找到這樣的SQL語句。
select /*+ rule */ s.sql_text from x$bh a,dba_extents b, (select * from (select addr from v$latch_children where name = 'cache buffers lru chain' order by sleeps desc) where rownum<11) c, v$sqltext s where a.hladdr = c.addr and a.dbarfil = b.relative_fno and a.dbablk between b.block_id and b.block_id + b.blocks and s.sql_text like '%'||b.segment_name||'%' and b.segment_type='TABLE' order by s.hash_value,s.address,s.piece /
3.2 buffer busy waits等待

      當一個session在讀取或修改buffer cache裏的內存數據塊時,首先必須獲得cache buffers chains latch,獲得以後,到hash chain上遍歷直到找到需要的buffer header後。這時,該session必須在該buffer header上以share或exclusive模式(具體哪個模式由該session的操作決定)獲得一個buffer lock或一個buffer pin。一旦buffer header被pin住,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列的值來共同診斷。如果p3爲130意味着同時有很多session在訪問同一個data block,而且該data block沒有在內存裏,而必須從磁盤上獲取。有三種方法可以降低該事件出現的頻率:
    a、降低併發性。這個比較難實現。
    b、找出並優化含有這些segment的SQL語句,以降低物理和邏輯讀。
    c、增加freelists和freelist groups。
如果沒有足夠的freelists,當同時對同一個表進行insert時,這就很容易引起buffer busy waits等待。如果正在等待buffer busy waits的session正在進行insert操作,那麼需要檢查以下那個表有多少freelists了。當然,由於freelists的不足主要會導致對於segment header的buffer busy waits等待。

    如果p3爲220意味着有多個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、增加爭用對象的freelists和freelist groups的數量。
    b、確定pctfree和pctused之間的間隔不要太小。
    c、確保next extent的尺寸不要太小。
    d、9i以後,使用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。這是應用系統的問題,在數據庫來說對此無能爲力。
3.3 buffer busy waits等待

    在一個數據塊被讀入buffer cache之前,oracle進程必須爲該數據塊獲得一個對應的可用的內存數
據塊。當session在LRU 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 waits的total_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秒。同時也可以看到,請求了290532493(free buffer requested)個可用的內存數據塊,但是在這個過程中只跳過了2983596(free buffer inspected)個數據塊,二者相差2個數量級。說明系統很容易就找到可用的內存數據塊。

   如果一個session花費了很多的時間等待free buffer waits等待事件的話,通常可能有以下原因:
    1) 低效率的SQL語句:對於那些引起很大邏輯讀的SQL語句(v$sql裏的disk_reads),那些SQL語句可能進行了全表掃描,索引全掃描、或者通過了不正確的索引掃描表等。調整這些SQL語句以降低邏輯讀。
    2) DBWR進程不夠多:也可以通過增加DBWR checkpoints的個數來降低free buffer waits。9i下,可以通過減小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的大小。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章