【分享】latch free等待事件(一)

yellowlee發表於2010-02-09

論壇的兄弟太好了,這幾個文件非常好,感謝!

Latch free等待事件

 

原文:oracle wait interface—a practical guide to performance diagnostics & tuning

Richmond shee

Kirtikumar deshpande

K gopalakrishnan

 

Mcgraw-hill/osborne

2100 powell street, 10th floor

Emeryville, california 94608

U.s.a.

Chapter 6: interpreting locks-related wait events - latch free

 

文件修訂歷史:

版本

時間

作者

1.00

2006-4-21

NinGoo

 

 

 

 

 

本文只是對原文的一個大概的翻譯,建議最好還是閱讀原文。

如果你發現有任何問題,歡迎反饋NinGoo@itpub.net

 


 

 

 

Latch free等待事件的三個引數:p1latch的地址;p2latch編號;p3-請求次數。從oracle10g起,latch free不再包含所有的latch等待,有些latch等待可能表現為單獨的等待事件,這個後面有提到一些這樣的等待事件,一般情況下我們還是統稱為latch free等待事件。在處理latch free等待事件時,需要注意以下幾點:

n         Latch只是用來保護sga中的記憶體結構。對資料庫中的物件的保護,使用的lock而不是latchOracle sga中有許多latch,用來保護sga中各種記憶體結構不會因為併發訪問而損壞。

n         等待latch的是oracle會話。不同的latch型別會導致會話採取不同的策略。

n         oracle9i(包括9i)之前,latch free等待事件包括了所有的latch等待,但從oracle10g起,latch被分成不同的種類,並且某些latch表現為獨立的等待事件。

 

什麼是latch

Latch是一種鎖機制。你應該已經熟悉latch的概念和用法,雖然可能你自己並沒有意識到。在日常的工作和交流中,latch都經常出現,比如你鎖門時,需要獲得一個latch;或者你坐到車裡,繫上安全帶,你就把自己放在一個latch的保護中了。

 

oracle中,latch是一種輕量級的鎖。一般來說,latch由三種記憶體元素組成:pid(程式id),記憶體地址和記憶體長度。Latch保證對共享資料結構的排它性訪問,以此來保證記憶體結構的完整性不受到損壞。在多個會話同時修改或者檢視(inspect)sga中同一個記憶體結構時,必須序列化訪問以保證sga中資料結構的完整性。

 

Latchlock的異同

Latchlock有許多不同之處。下表列出了latchlock之間的比較結果。

 

Latch

Lock

目的

只有一個目的:保證對記憶體結構的排他性訪問(從oracle9i開始,cache buffers chain latch可以允許只讀共享訪問)

兩個目的:如果鎖模式是相容的,允許多個程式共享相同的資源;如果鎖模型是不相容的,保證對共享資源的排它性訪問。

適用場景

只能應用於sga中的資料結構,保護記憶體物件。Latch隻影響單次操作,而和事務無關。

保護資料庫物件,諸如表,資料塊和狀態物件等。由應用程式驅動,控制對資料庫中資料和後設資料的訪問。

Lock是事務性的。

獲取方式

兩種模式:willing-to-waitno-wait

六種模式:null, row share, row exclusive, share, share row exclusiveexclusive

範圍

資訊都儲存在記憶體中,並且只在本例項可見――latch是例項級別的

 資訊儲存在資料庫中,並且該資料庫的所有例項都可見――lock是資料庫級的

複雜度

使用簡單機器指令比如:test-and-set, compare-and-swap或其他簡單的cpu指令實現。由於cpu指令平臺相關,所以latch在不同的平臺的具體實現不一樣。

輕量級的。

需要上下文切換(context siwtch),使用一系列指令實現。

重量級的。

持續事件

非常短暫(通常是微妙級的)

通常在整個事務中都持有。

排隊機制

當一個程式獲取latch失敗,轉入睡眠狀態時,他的請求不需要按順序排隊(一個例外情況:latch wait list latch需要排隊)。

當一個程式獲取lock失敗,它的請求會進入一個佇列,除非指定nowait

死鎖

Latch的實現方式不會產生死鎖(不排隊)

Lock的排隊機制可能導致死鎖。死鎖發生時會產生相應的跟蹤檔案。

 

Latch家族

Latch有三種:父latch,子latch和獨立latch。父latch和獨立latchoracle的核心程式碼中固化,子latch則在例項啟動時創造。V$latch_parentv$latch_children檢視分別包含父latch和子latch的統計資訊。而v$latch則包含獨立latch,父latch及其相應子latch的聚合統計資訊。

 

Latch的獲取

程式獲取latch有兩種模式:willing-to-waitno_waitNo-wait模式只在少數latch中使用。通過no-wait模式獲取latch的統計資訊記錄在immediate_getsimmediate_misses列中,這些列在v$latchv$latch_parentv$latch_children檢視中都存在。一般來說,no-wait模式在第一次獲取一些有很多子latchlatch比如redo copy時使用。如果一個程式第一次獲取這些子latch中的任何一個失敗,它會立即使用no-wait模式詢問下一個。只有當採用no-wait模式試圖獲取所有的子latch都失敗以後,才會轉而採用willing-to-wait模式。

 

通過willing-to-wait模式獲取latch的統計資訊存放在getsmisses列中。每當一個程式用willing-to-wait模式去獲取一個latch時,gets都會增加。

 

如果程式在第一次請求latch時,latch可用,就會直接獲得該latch。在修改任何受到保護的資料結構之前,程式會將一些恢復資訊寫入到latch恢復區,這樣當獲得latch的程式發生異常時,pmon程式才能夠清理該程式持有的latch

 

如果請求latch時,該latch不可用,程式就會在cpu中等待一小段時間(spin)然後重新請求latch。如果latch一直不可用,該過程(spin一段時間然後重新請求)會一直重複。重複的次數由隱含引數_spin_count決定,預設值2000。如果在請求_spin_count次之內獲得了latch,就對spin_getsmisses列各加一,否則,程式在v$session_wait中記錄latch free等待事件,然後釋放cpu,轉入睡眠狀態。睡眠一定時間後,程式被喚醒並重覆上面的過程,一直到獲得latch。在成功獲得latch後,才會更行sleep列得統計資訊。

 

由於程式只有在獲得latch後才會停止對latch得請求,如果某個持有latch的程式發生異常,其他請求該latch的程式該怎麼辦?豈不是要一直等待下去?不會的。當一個程式請求latch失敗一定次數後,它會請求pmon程式檢視該latch的持有者,如果持有程式異常,pmon就會清理該程式,釋放latch

 

每個latch都有一個從013的優先順序編號。父latch和獨立latch的優先順序編號是在oracle核心程式碼中固定的。子latch是÷在例項啟動時建立,其優先順序編號從其父latch繼承。使用優先順序可以避免死鎖。

n         當一個程式請求no-wait模式的latch時,該latch的優先順序編號必須和它當前已經持有的latch的優先順序編號相同。

n         當一個程式請求willing-to-wait模式的latch時,該latch的優先順序編號必須比它當前已經持有的latch的優先順序編號要大。

 

短等待latch與長等待latch

大多數latch都是短等待latch,所以,程式請求latch時不會等待太長的時間。Oracle程式請求latch失敗而導致進入睡眠狀態,每次睡眠時間按雙指數佇列增長,比如睡眠時間可能像下面的佇列一樣:1,1,2,2,4,4,8,8,16,16,32,32,64,64(釐秒)……,最長的睡眠時間由隱含引數_max_ exponential_sleep,預設2秒。但是如果一個程式當前已經持有其他的latch,則最長睡眠時間會減少為_max_sleep_holding_latch,預設值4釐秒。這樣,如果一個程式已經持有latch,就不允許睡眠太長的時間,否則可能會使其他等待該程式所持有的latch的程式的等待時間過長。

 

有小部分latch屬於長等待latch,這意味著這些latch被可能長久持有。如果請求該latch的程式進入睡眠狀態,需要其他程式來喚醒,這會產生一個latch wait posting等待事件,該行為由隱含引數_latch_wait_posting控制。在oracle8i,只有2個長等待latch,如下面的示例sqloracle9ioracle10g有更多長等待latch)所示。_latch_wait_posting引數從oracle9i起已經廢棄,使用latch wait postinglatch的統計資訊被記錄在waiters_woken列中。

Select name, immediate_gets, immediate_misses, 
       gets, misses, sleeps, waiters_woken 
From   v$latch 
Where waiters_woken > 0;
 
                immediate  immediate                                 waiters
Name               gets      misses    gets    misses   sleeps   woken
------------------------ --------------- -------------------- ----------------- ------------- --------------- ---------------
Shared pool             0             0     18464156     3124     1032       485
Library cache        85508           124  1564400540  4334362  1516400   690419

 

Latch分類

oracle9iR2開始,latch可以被分成不同的型別,每一類latch都可以有不同的_spin_count值。在早期版本中,如果改變_spin_count值,會對系統中所有的latch造成影響。這樣可能會增加cpu的負擔,而latch分類則正是為解決這個問題而引入的。例如,如果cache buffers chains latchsleep次數很多,而且cpu資源充足,我們就可以將cache buffer chains latch所在的分類的_spin_count的值調高。高_spin_count值可以降低sleepsmisses的次數,代價是花費更多cpu時間。內部檢視x$ksllclass (kernel serverice lock latches class)包含了latch的所有八種型別的資訊。其中indx列就是latch型別編號。

Select indx, spin, yield, waittime
from   x$ksllclass;

      indx       spin      yield   waittime
---------- ---------- ---------- ----------
         0      20000          0          1
         1      20000          0          1
         2      20000          0          1
         3      20000          0          1
         4      20000          0          1
         5      20000          0          1
         6      20000          0          1
         7      20000          0          1

8 rows selected.

 

x$ksllclass中的每行記錄都和一個隱藏引數_latch_class_n關聯,通過這些隱含引數,你可以改變相應的_spin_countyieldwaittime的值(x$檢視不能由使用者手動更新)。例如,latch型別0由引數_latch_class_0控制,latch型別1由引數_latch_class_1控制。如果你想將cache buffers chains latch_spin_count值改成10,000,首先你需要知道latch的編號,通過以下查詢可以獲得

Select latch#, name 
From   v$latchname 
Where name = ’cache buffers chains’;
 
    latch# name
---------- -------------------------------
        97 cache buffers chains
 

然後,你需要修改init.ora中的下面2個引數:

_latch_class_1 = "10000"

_latch_classes = "97:1"

第一個引數_latch_class_1將型別1spin_count值改為10,000

第二個引數_latch_classes 將編號為97latch分配到型別1

 

再次查詢x$ksllclass,我們可以發現:

Select indx, spin, yield, waittime 
From   x$ksllclass;
 
      indx       spin      yield   waittime
---------- ---------- ---------- ----------
         0      20000          0          1
         1      10000          0          1
         2      20000          0          1
         3      20000          0          1
         4      20000          0          1
         5      20000          0          1
         6      20000          0          1
         7      20000          0          1
8 rows selected.

 

Select a.kslldnam, b.kslltnum, b.class_ksllt 
From   x$kslld a, x$ksllt b 
Where  a.kslldadr = b.addr 
And    b.class_ksllt > 0;
 
Kslldnam                    kslltnum class_ksllt
------------------------- ---------- -----------
Process allocation                 3           2
Cache buffers chains              97           1

 

注意:如果伺服器的cpu資源緊張,請不要增加_spin_count的值。當然,預設值2000是很久以前定下來的值,當時的cpu比現在的cpu要慢得多。

 

Latch free等待事件可以告訴我們什麼?

如果我們在v$session_wait中發現有latch free等待事件,就意味著,程式在請求一個willing_to_wait模式的latch,在重試了_spin_count次後還是沒有獲得latch,然後轉入睡眠狀態了。如果latch爭用嚴重,將會由於不斷的spin導致cpu資源緊張,從而增加系統響應時間。

 

V$system_event檢視的total_waits列記錄了程式獲取willing-to-wait模式latch失敗的次數。V$latchsleeps列記錄了程式由於等待某個latch而進入睡眠狀態的次數。由於一個程式在_spin_count次嘗試請求latch失敗後會轉入睡眠狀態,total_waits列應該等於sleeps列的值的和,如以下sql所示。但是,某些時候,total_waits會比sleeps的和要大,這是因為,只有在程式獲得latch後才會更新total_waits的值,而不是每次請求latch失敗就更新。

Select a.total_waits, b.sum_of_sleeps
from  (select total_waits from v$system_event where event = ’latch free’) a,
      (select sum(sleeps) sum_of_sleeps from v$latch) b;


total_waits sum_of_sleeps
----------- -------------
  414031680     414031680

 

由於latch free等待時間一般較短,所以在很少一段時間內,total_waits就可能變得非常大,這並不一定意味著系統有問題。只有當time_waited的值也非常顯著的時候,你才需要關注latch free等待事件。

 

Latch失敗區域(latch miss locations

V$latch_misses檢視儲存了latch失敗在oracle核心程式碼中的區域資訊。這些資訊對於oracle support診斷latch等待事件有幫助。你可以通過以下查詢檢視位置資訊。Steve adams有篇非常棒的關於這方面的文章http://www.ixora.com.au/newsletter/2001_02.htm

Select location, 
       parent_name,
       wtr_slp_count,
       sleep_count,
       longhold_count
from   v$latch_misses
where  sleep_count > 0
order by wtr_slp_count, location;
                                                                    longhold
location             parent_name          wtr_slp_count sleep_count    count
-------------------- -------------------- ------------- ----------- --------
. . .
Kglupc: child        library cache              7879693    11869691        0
kghupr1              shared pool                8062331     5493370        0
kcbrls: kslbegin     cache buffers chains       9776543    14043355        0
kqrpfl: not dirty    row cache objects         15606317    14999100        0
kqrpre: find obj     row cache objects         20359370    20969580        0
kglhdgn: child:      library cache             23782557     9952093        0
kcbgtcr: fast path   cache buffers chains      26729974    23166337        0
kglpnc: child        library cache             27385354     7707204        0

 

Oracle10gR1中的latch

Oracle10g之前,所有的latch等待都顯示為latch free等待事件。你可以通過latch free事件的p2引數和v$latch.latch#關聯或者通過10046事件來查詢某個程式爭用的是哪個latch。而在Oracle10g中,latch被分成許多獨立的等待。下面是oracle10gR1latch一個列表:

Select name
from   v$event_name
where  name like ’latch%’
order by 1;

name
----------------------------------------------------------------
latch activity
latch free
latch: in memory undo latch
latch: kcl gc element parent latch
latch: mql tracking latch
latch: cache buffer handles
latch: cache buffers chains
latch: cache buffers lru chain
latch: checkpoint queue latch
latch: enqueue hash chains
latch: gcs resource hash
latch: ges resource hash list
latch: latch wait list
latch: library cache
latch: library cache lock
latch: library cache pin
latch: messages
latch: object queue header heap
latch: object queue header operation
latch: parallel query alloc buffer
latch: redo allocation
latch: redo copy
latch: redo writing
latch: row cache objects
latch: session allocation
latch: shared pool
latch: undo global data
latch: virtual circuit queues
28 rows selected.

 

Latch產生的原因,診斷以及對策

Latch爭用通常意味這某個程式持有latch的時間過長。如果latch爭用明顯,系統效能將顯著下降。在高併發的環境中,latch爭用經常發生,並且你無法完全消除latch爭用。在v$system_event中總會出現latch free等待事件。只有當time_waited相對例項啟動以來的總時間比較明顯時,你才需要關注latch爭用。當latch在系統範圍內的等待時間比較顯著時,你可以通過v$latch中的sleeps列來發現爭用顯著的latch

Select name, gets, misses, immediate_gets, immediate_misses, sleeps 
from   v$latch
order by sleeps;
                                             immediate immediate
name                       gets     misses        gets    misses     sleeps
-------------------- ---------- ---------- ----------- --------- ----------
enqueue hash chains    42770950       4279           0         0       1964
shared pool             9106650       5400           0         0       2632
row cache objects      69059887      27938         409         0       7517
enqueues               80443314     330167           0         0      13761
library cache          69447172     103349      465827       190      44328
cache buffers chains 1691040252    1166249    61532689      5909     127478
. . .

對不同的latch,其產生的原因以及可採取的對策都有所不同。詳細的說明所有的latch可以寫成一本書了。這裡我們只選擇最常見的五個latch加以說明:shared pool, library cache, cache buffers chains, cache buffers lru chainrow cache objects

 

Shared poollibrary cache latch

Oracle的共享池由不同的結構組成。主要包括:資料字典快取,sql區和庫快取。通過v$sgastat你可以檢視其他一些結構。Shared pool latch主要用來保護共享池的記憶體結構,當分配或者釋放共享池記憶體時需要先獲得該latch。例如,為一個新的sql語句或pl/sql過程、函式、包,觸發器等分配空間(硬解析)時,或者為換出、清除某些記憶體塊,以便為新的物件騰出足夠的空間時,都需要獲取shared pool latch

 

oracle9i之前,共享池記憶體結構由一個獨立shared pool latch保護,從9i開始,則有最多7個子latch可以用於共享池的保護。這也是為什麼oracle9i可以將共享池分成多個子共享池的原因(伺服器至少需要4cpu,並且shared_pool_size大於250m才能使用多個子共享池的特性)。子共享池的個數可以通過隱含引數_kghdsidx_count手動調節,該引數同時會指定合適的shared poollatch的個數。如果你手動增加子共享池的個數,你應該同時增加shared_pool_size的值,因為每個子共享池都有自己的結構,lru列表和shared pool latch。否則,例項啟動時可能會遇到以下錯誤:

Ora-04031: unable to allocate 32 bytes of shared memory ("shared pool","unknown object","sga heap(5,0)","fixed allocation callback").

 

下面的統計資訊是從一個16cpushared_pool_size256moracle9i資料庫中讀取的。由_kghdsidx_count引數可知共享池被分成2個子池,通過x$kghlu(kernel generic heap lru)可以知道lru列表也有2個。v$latch_children檢視顯示了7個子latch中的2個已經被使用。

 

Select a.ksppinm, b.ksppstvl 
from   x$ksppi a, x$ksppsv b
where  a.indx = b.indx
and    a.ksppinm = ’_kghdsidx_count’;

ksppinm            ksppstvl
------------------ ----------
_kghdsidx_count    2

select addr, kghluidx, kghlufsh, kghluops, kghlurcr, kghlutrn, kghlumxa
from   x$kghlu;

addr             kghluidx   kghlufsh   kghluops kghlurcr kghlutrn   kghlumxa
---------------- -------- ---------- ---------- -------- -------- ----------
80000001001581b8        2   41588416  496096025    14820    17463 2147483647
8000000100157e18        1   46837096 3690967191    11661    19930 2147483647

select addr, name, gets, misses, waiters_woken
from   v$latch_children
where name = ‘shared pool’;

addr             name                 gets     misses waiters_woken
---------------- ------------- ----------- ---------- -------------
c00000004c5b06b0 shared pool             0          0             0
c00000004c5b0590 shared pool             0          0             0
c00000004c5b0470 shared pool             0          0             0
c00000004c5b0350 shared pool             0          0             0
c00000004c5b0230 shared pool             0          0             0
c00000004c5b0110 shared pool    1385021389   90748637      12734879
c00000004c5afff0 shared pool    2138031345  413319249      44738488

 

庫快取中主要儲存遊標,sql語句,執行計劃,分析樹等。這些結構由library cache latch保護。當oracle程式修改、檢查、銷連線(pinning)、鎖定、裝載,或者執行庫快取中的結構時,都需要先獲得library cache latch。通過查詢v$latch_children可以得知當前例項中的library cachelatch的個數。通常應該為大於cpu個數的最小質數,該值由隱含引數_kgl_latch_count控制。從oracle9i開始,v$sqlarea檢視增加了一個child_latch列,用來指示遊標在各個library cache latch是如何分佈的。

Select count(*)
from   v$latch_children
where  name = ‘library cache’;

 

Shared pool library cache latch爭用原因一 ―― 分析

Shared poollibrary cache latch爭用通常是由於硬分析引起。硬分析需要分配新的遊標,或者將已經換出的遊標重新執行。硬分析過多說明sql語句沒有充分繫結變數。硬分析是代價十分昂貴的操作,在分析期間需要一直持有ibrary cache latch

n         通過下列查詢可以發現系統中是否存在大量硬分析。軟分析數則可以用總分析數減去硬分析數獲得

Select a.*, sysdate-b.startup_time days_old 
from   v$sysstat a, v$instance b
where a.name like ‘parse%’;

statistic# name                      class      value   days_old
---------- ------------------------- ----- ---------- ----------
       230 parse time cpu               64   33371587  4.6433912
       231 parse time elapsed           64   63185919  4.6433912
       232 parse count (total)          64 2137380227  4.6433912
       233 parse count (hard)           64   27006791  4.6433912
       234 parse count (failures)       64      58945  4.6433912
備註:分析失敗可能是由於“ora-00942: table or view does not exist”錯誤或者共享記憶體不足。
 

n         檢視當前會話是否有大量硬分析

Select a.sid, c.username, b.name, a.value, 
       round((sysdate - c.logon_time)*24) hours_connected
from   v$sesstat a, v$statname b, v$session c
where  c.sid        = a.sid
and    a.statistic# = b.statistic#
and    a.value      > 0
and    b.name       = ‘parse count (hard)’
order by a.value;

 sid username   name                    value hours_connected
---- ---------- ------------------ ---------- ---------------
 510 sys        parse count (hard)         12               4
 413 pmappc     parse count (hard)        317              51
  37 pmhcmc     parse count (hard)      27680             111
 257 pmappc     parse count (hard)      64652              13
 432 pmappc     parse count (hard)     105505              13

 

oracle10g中,通過v$sess_time_model檢視中對硬分析和失敗分析的時間統計資訊,可以知道硬分析的來源。下面的例子顯示了某個會話的v$sess_time_model資訊。

Select * 
From   v$sess_time_model 
Where  sid = (select max(sid) from v$mystat);
 
 sid    stat_id stat_name                                             value
---- ---------- ------------------------------------------------ ----------
 148 3649082374 db time                                            11141191
 148 2748282437 db cpu                                              9530592
 148 4157170894 background elapsed time                                   0
 148 2451517896 background cpu time                                       0
 148 4127043053 sequence load elapsed time                                0
  148 1431595225 parse time elapsed                                  3868898
  148  372226525 hard parse elapsed time                             3484672
 148 2821698184 sql execute elapsed time                            9455020
 148 1990024365 connection management call elapsed time                6726
  148 1824284809 failed parse elapsed time                                 0
  148 4125607023 failed parse (out of shared memory) elapsed time          0
  148 3138706091 hard parse (sharing criteria) elapsed time            11552
  148  268357648 hard parse (bind mismatch) elapsed time                4440
 148 2643905994 pl/sql execution elapsed time                         70350
 148  290749718 inbound pl/sql rpc elapsed time                           0
 148 1311180441 pl/sql compilation elapsed time                      268477
 148  751169994 java execution elapsed time                               0

上面的分析統計資訊可以按照下面的方法分組:

1. parse time elapsed

               2. hard parse elapsed time

                               3. hard parse (sharing criteria) elapsed time

                                              4. hard parse (bind mismatch) elapsed time

2. failed parse elapsed time

               3. failed parse (out of shared memory) elapsed time

 

n         確定系統中的常量sql語句(literal sql),它們往往都是可以使用並且應該使用繫結變數的。下面通過查詢v$sqlarea檢視,列出超過4個執行例項的sql語句的前40個字元,這裡假設你的系統中前40個字元相同的語句被認為是沒有使用繫結變數的常量sql。很明顯,如果使用更長的字串或者更多的執行例項作為過濾條件,得到的結果sql語句會少很多。然後你可以根據得到的結果,建議程式開發人員對這些常量sql語句儘量使用繫結變數。

Select hash_value, substr(sql_text,1,80)
from   v$sqlarea
where  substr(sql_text,1,40) in (select substr(sql_text,1,40)
                                 from   v$sqlarea
                                 having count(*) > 4
                                 group by substr(sql_text,1,40))
order by sql_text;

hash_value substr(sql_text,1,80)
---------- -----------------------------------------------------------------
2915282817 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
2923401936 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
 303952184 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
 416786153 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
2112631233 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
3373328808 select region_id from person_to_chair where chair_id = 988947
 407884945 select region_id from person_to_chair where chair_id = 990165
3022536167 select region_id from person_to_chair where chair_id = 990166
3204873278 select region_id from person_to_chair where chair_id = 990167
 643778054 select region_id from person_to_chair where chair_id = 990168
2601269433 select region_id from person_to_chair where chair_id = 990169
3453662597 select region_id from person_to_chair where chair_id = 991393
3621328440 update plan_storage set last_month_plan_id = 780093, pay_code
2852661466 update plan_storage set last_month_plan_id = 780093, pay_code
 380292598 update plan_storage set last_month_plan_id = 780093, pay_code
2202959352 update plan_storage set last_month_plan_id = 780093, pay_code
. . .

oracle9i中,也可以通過如下的語句查詢v$sql檢視中使用了相同的執行計劃的sql語句,這些語句也可能是常量sql語句。

Select plan_hash_value, hash_value
from   v$sql
order by 1,2;

 

如果你的系統中存在大量的常量sql語句,當你將它們改為充分使用繫結變數後,對shared pool latchlibrary cache latch的爭用將會顯著減少。更改sql語句,使用繫結變數,這通常需要修改應用程式。另外一個不需要改動應用的方法是,修改初始化引數cursor_sharing,將其值改為force(注:原文如此。一般情況下請儘量使用similar而不是force),這個引數允許系統對一些只有常量值不一樣的sql語句共享遊標,以減少latch爭用、記憶體佔用和硬分析。

注意:在oracle8i早期版本中,使用cursor_sharing可能導致bug。在使用了物化檢視的環境中,請慎用該引數,否則可能導致長久的library cache pin等待。另外,使用cursor_sharing = force可能導致優化器生成錯誤的執行計劃。這或多或少的會對系統效能造成影響。從oracle9i起,優化器可以通過窺視pga中的資訊來進行變數繫結,以此生成合適的執行計劃,該特性可以通過隱含引數_optim_peek_user_binds來開啟,並且該特性只對那些需要硬分析的sql語句有效,這意味著會基於繫結變數的第一個值來生成執行計劃。

當一個新的sql語句到達時,oracle首先在庫快取中檢查是否已經有相同的語句存在。如果已經存在,則可以花費較小的代價執行該語句,這就是所謂的軟分析。硬分析通常意味著較壞的效能,而軟分析過多也不是什麼好事。在軟分析期間,需要持有library cache latch,並且oracle依然需要對語句進行語法和語義檢查,除非該語句已經在會話的遊標快取中。你可以通過設定引數session_cached_cursors來減少library cache latch的持有時間(具體資訊請檢視oracle metalin,編號#30804.1 #62143.1)。但是,減少軟分析的最佳方法還是優化應用程式。最好是分析一次,執行多次(很像java的宣傳口號),而不要分析一次,執行一次。你可以通過v$sqlareaparse_calls列來查詢分析過多的sql語句。

 

Shared pool latch爭用原因二 ―― 過大的共享池

oracle9i起,由於引入了多個子共享池的特性,過大的共享池不再是一種壞事。在9i之前,過大的共享池通常會引起shared pool latch爭用。共享池中可用記憶體分成不同的記憶體塊(chunk),不同大小範圍的塊由不同的可用列表(freelist)來管理。在共享池中分配空間時,需要掃描可用列表,掃描期間,需要持有shared pool latch。過大的共享池會使得可用列表過長,從而使得shared pool latch的持有時間變長。在高併發環境中,latch持有時間過長就可能造成latch爭用(表現為較高的sleepsmisses值),尤其是大量使用常量sql的系統,對這樣的系統,不要一味想著加大共享池,更重要的是想一想你為什麼會需要儲存這麼多不能共享的語句到共享池中。

 

通過alter session set events ’immediate trace name heapdump level 2’可以轉存共享池資訊,從中可以看到共享池的可用列表資訊。在生成的跟蹤檔案中查詢bucket,你可以發現記憶體塊(chunk)分配到不同的bucket上。另外,你也可以通過下面的方法生成一個查詢來列出共享池的可用記憶體管理資訊。該查詢只要生成一次,就可以在生成該跟蹤檔案的資料庫中重複使用,但不要在其他不同版本的資料庫中使用,否則可能得到錯誤的結果。該查詢在oracle10gR1中不可用,因為它限制了case分支數不能超過128(具體資訊參考oracle metalink 編號#131557.1bug#3503496),或者你可以通過使用decodesign函式來完成該功能。

Sql> oradebug setmypid
statement processed.
Sql> oradebug dump heapdump 2
statement processed.
Sql> oradebug tracefile_name
/u01/admin/webmon/udump/orcl_ora_17550.trc
sql> exit
(注:上面是使用oradebug獲得轉存檔案,也可以使用alter session set events ’immediate trace name heapdump level 2’)
$ grep bucket /u01/admin/webmon/udump/orcl_ora_17550.trc > tmp.lst
$ sed ’s/size=/ksmchsiz>=/’ tmp.lst > tmp2.lst
$ sed ’s/ bucket //’ tmp2.lst | sort –nr > tmp.lst

# 通過shell指令碼生成查詢
echo ’select ksmchidx, (case’
cat tmp.lst | while read line
do
  echo $line | awk ’{print "when " $2 " then " $1}’
done
echo ’end) bucket#,’
echo ’       count(*) free_chunks,’
echo ’       sum(ksmchsiz) free_space,’
echo ’       trunc(avg(ksmchsiz)) avg_chunk_size’
echo ’from   x$ksmsp’
echo "where  ksmchcls = ’free’"
echo ’group by ksmchidx, (case’;
cat tmp.lst | while read line
do
  echo $line | awk ’{print "when " $2 " then " $1’}
done
echo ’end);’

如果你發現資料庫的共享池可用列表過長,並且系統中使用了常量sql,你或許應該考慮減少shared_pool_size。這會降低系統中對shared pool latch的爭用。但要注意共享池不能太小,否則可能導致ora-04031錯誤。另外,通過使用dbms_shared_pool.keep將常用物件釘在記憶體中也是個好主意,v$db_object_cache中儲存了釘在記憶體中的物件的資訊。

 

Library cache latch爭用原因三 ―― 語句版本數過多

對於字元完全一致但是由於引用不同的物件而不能共享的sql語句,oracle使用多個子遊標來指向該語句的不同版本。例如,系統中有三個名叫customer的表,但是屬於不同的模式。則對於語句select * from customer,不同的模式執行該語句,語句字元上完全一樣,其hash值完全一樣,但是該語句無法共享,因為它引用的物件不同。所以會生成該語句的不同子版本。當一個sql語句有多個子版本時,oracle需要比較該語句的所有存在的子版本,在此期間需要持有library cache latch,這樣可能導致library cache latch爭用。解決這種情況也很簡單,在系統中,儘量不要使用相同的物件名。下面的查詢列出了v$sqlarea中子版本超過20的所有sql語句:

Select version_count, sql_text
from   v$sqlarea
where  version_count > 20
order by version_count, hash_value;

注意:在oracle8i中,語句的版本過多,可能導致和sql執行監控相關的bug(參考oracle metalink 編號#62143.1)。這個bug會導致sql無法共享。可以通過將隱含引數_sqlexec_progression_cost設定為0來禁用sql執行監控特性,該引數同時會禁止v$session_longops中的資料。

 

Oracle提供了檢視v$sql_shared_cursor,其中可以看到為什麼無法共享一個已經存在子游標。每個列都限制了遊標無法共享的一個原因。

-- 大寫的列是oracle9i起才有列.
-- oracle 10g R1
還有其他8個列
select a.*, b.hash_value, b.sql_text
from   v$sql_shared_cursor a, v$sqltext b, x$kglcursor c
where  a.unbound_cursor         || a.sql_type_mismatch     ||
       a.optimizer_mismatch     || a.outline_mismatch      ||
       a.stats_row_mismatch     || a.literal_mismatch      ||
       a.sec_depth_mismatch     || a.explain_plan_cursor   ||
       a.buffered_dml_mismatch  || a.pdml_env_mismatch     ||
       a.inst_drtld_mismatch    || a.slave_qc_mismatch     ||
       a.typecheck_mismatch     || a.auth_check_mismatch   ||
       a.bind_mismatch          || a.describe_mismatch     ||
       a.language_mismatch      || a.translation_mismatch  ||
       a.row_level_sec_mismatch || a.insuff_privs          ||
       a.insuff_privs_rem       || a.remote_trans_mismatch ||
       a.logminer_session_mismatch || a.incomp_ltrl_mismatch    ||
       a.overlap_time_mismatch     || a.sql_redirect_mismatch   ||
       a.mv_query_gen_mismatch     || a.user_bind_peek_mismatch ||
       a.typchk_dep_mismatch       || a.no_trigger_mismatch     ||
       a.flashback_cursor <> ’nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn’
and    a.address = c.kglhdadr
and    b.hash_value = c.kglnahsh
order by b.hash_value, b.piece;

 

Cache buffers chains latch

當一個資料塊讀入到sga中時,該塊的緩衝區頭(buffer header)會放置在一個hash bucket的連結串列(hash chain)中。該記憶體結構由一系列cache buffers chainslatch保護(又名hash latch或者cbc latch)。下圖描述了hash latchhash bucketbuffer headerhash chain的關係

圖一. Oracle8i以上的資料緩衝區示意圖

一個程式要新增,刪除,查詢,檢視,讀取或者修改hash chain上的塊,必須先獲得cache buffers chains latch,以保證對該chain的排他訪問,為保證完整性,必須犧牲併發性。

注:從oracle9i開始,對cache buffer chains latch可用只讀共享訪問,這可以減少部分爭用,但並不能完全消除爭用。

 

一個特定的塊頭具體分配到哪個hash bucket,是通過dba(data block address)和隱含引數_db_block_hash_buckets實現的。例如,hash bucket = mod(dba, _db_block_hash_buckets)。通過查詢v$bhx$bh檢視可以發現緩衝區頭的爭用,也可以通過以下語句轉存緩衝區頭的資訊:

Alter system set events ’immediate trace name buffers level 1’;

oracle8.0之前,每一個hash bucket都有一個cache buffers chains latchhash latch),並且hash bucket都只有一個hash chain連結串列。換句話說,hash latch,hash backethash chain之間是1:1:1的關係。預設的hash bucket個數為大於db_block_buffers / 4的最小質數,通過隱含引數_db_block_hash_buckets可以修改該值。例如,假如db_block_buffers = 50000,則該例項中有12501hash latch,有12501hash bucket,有12501hash chain

oracle8i開始,oraclehash latchhash bucket之前的關係改成了 1:m,但hash buckethash chain之間還是1:1,也就是一個hash latch可以同時保護多個hash chain連結串列。這樣,可以顯著的減少系統中hash latch的個數。Hash latch的預設個數還是和db_block_buffers的值相關。當資料緩衝區小於1G時,一般都是1024個。通過隱含引數_db_blocks_hash_latches可以修改該值。下面的語句查詢例項中的hash latch數目:

Select count(distinct(hladdr)) 
from   x$bh;

Count(distinct(hladdr))
-----------------------
                   1024

Select count(*) 
from   v$latch_children
where  name = ’cache buffers chains’;

  count(*)
----------
      1024

Hash bucket的預設個數等於2 * db_block_buffers,可以通過隱含引數_db_block_hash_buckets修改。這樣,假如db_block_buffers=50000,則系統中有100000hash bucket100000hash chain,但是隻有1024hash latch(假設塊大小為8k)。由此可以看出,oracle8ioracle8.0中,hash latch數目發生了顯著的變化。許多DBA認為,由於hash latch數目顯著減少,可能會導致latch爭用的增加。但是,oracle解釋說,通過以8為因子增加hash chain連結串列的個數,單個連結串列會比以前變得更短,這樣每次cache buffer chains latch的持有時間也變得更短,以此來補償latch個數減少帶來的latch爭用。但不要依賴這個機制,你還是會看到很多latch爭用。

 

Oracle10g使用了不同的演算法來決定系統中hash bucket的預設個數。一開始好像等於db_cache_size / 4。但後來的測試證明,在某些不同的db_cache_size值範圍內,例如當db_cache_size132m260m之間時,hash bucket的個數是一個常數。下面的表中列出了從oracle8ioracle10g,不同的資料緩衝大小時的hash bucket的預設個數,其中資料塊大小都是8k

Oracle 10g  作業系統solaris

Db_cache_size

32m

64m

128m

256m

512m

1024m

2048m

_ksmg_granule_size

4m

4m

4m

4m

4m

16m

16m

_db_block_buffers

3976

7952

15904

31808

63616

127232

254464

_db_block_hash_buckets

8192

16384

32768

65536

131072

262144

524288

_db_block_hash_latches

1024

1024

1024

1024

1024

1024

2048

Oracle9i 作業系統solaris

Db_cache_size

32m

64m

128m

256m

512m

1024m

2048m

_ksmg_granule_size

4m

4m

16m

16m

16m

16m

16m

_db_block_buffers

4000

8000

16016

32032

64064

128128

256256

_db_block_hash_buckets

8009

16001

32051

64067

128147

256279

512521

_db_block_hash_latches

1024

1024

1024

1024

1024

1024

2048

Oracle8i 作業系統solaris

Db_block_buffers

4000

8000

16016

32032

64064

128128

192192

_db_block_hash_buckets

8000

16000

32032

64064

128128

256256

384384

_db_block_hash_latches

1024

1024

1024

1024

1024

1024

2048

 

Cache buffers chains latch爭用原因一 ―― 低效的sql語句

低效的sql語句是導致cache buffers chains latch爭用的主要原因。在高併發系統中, atch free時間可能因此非常明顯。典型的情況是,應用程式開啟多個併發會話執行相同的低效sql,並且訪問同樣的資料集。

 

你應該時刻銘記下面三點:

n         每次邏輯讀都需要請求一次latch

n         只有獲得某個latch之後才會停止對該latch的不斷請求。

n         在某個時刻,只有一個程式可以獲得cache buffers chains latch,而該latch可能用於保護很多的資料塊,其中的某些塊可能正在被其他程式請求(當然,前面也已經提過,oracle9i允許只讀性質的cache buffers chains latch共享)。

 

一般而言,較少的邏輯讀意味著較少的latch請求,也就意味著較少的latch爭用和更好的系統效能。所以,你應該找出導致cache buffers chains latch爭用的低效sql語句,優化這些語句,儘量降低其邏輯讀。那些buffers_get/executions比值較大的sql可能就是你需要調整的語句。

1:某些dba可能通過修改隱含引數_db_blocks_hash_latches來增加系統中cache buffers chains latch的個數,而不是首先去優化低效的sql語句,這是不正確的,增加latch能暫時降低對latch的爭用,但這是治標不治本的方法。

2:在sun solareis平臺上,我們將一個資料庫從oracle8.1.7.4升級到oracle9.2.05之後,發現了大量的cache buffers chains latch爭用,新的優化器為應用程式生成了低效的執行計劃。一些隱藏的優化器相關的引數,在oracle8i中是無效的,但在oracle9i中有效。在重設這些引數後,問題得意解決。如果你遭遇到同樣的情況,建議請求oracle的技術支援。

 

Cache buffers chains latch爭用原因二 ―― 熱點塊

熱點塊是導致cache buffers chains latch爭用的另外一個主要原因。當多個程式重複訪問一個或多個由同一個cache buffers chains latch保護的塊時會導致該問題。這通常是應用程式引起的。在這種情況下,增加cache buffers chains latch的個數對熱點塊導致的爭用沒有什麼作用。因為資料塊分佈在哪個hash buckethash chain上是由塊地址(dba:data block address)hash bucket的個數決定的,和hash latch的個數沒有關係。只要塊地址和hash bucket數沒有改變,這些熱點塊還是會分佈在原來的hash buckethash chain上,還是由原來的hash latch保護,那麼就還會對這些hash latch產生爭用。除非系統中latch數目顯著的增加(這樣每個latch管理的hash bucket就會很少,甚至一個latch管理一個hash bucket,這樣原來的熱點塊可能就會有其他的幾個latch來管理,而不再需要爭用原來的那個latch)。

 

解決這樣的cache buffers chains latch爭用,最好的方法是找出熱點塊。通過latch free等待事件的p1raw引數可以知道是否是因為熱點塊導致了latch爭用。(在oracle10g中,cache buffers chains latch的相關等待事件不再是latch free,而是cache buffers chains)。P1raw引數是latch的地址。如果多個會話都在等待同一個latch地址,那麼恭喜你遇到熱點塊問題了。下面的例子中,可以發現由地址為00000400837d7800 00000400837de400latch保護的hash chain中存在熱點塊(多個會話都在等待這兩個地址的latch)。

Select sid, p1raw, p2, p3, seconds_in_wait, wait_time, state
from   v$session_wait
where  event =’latch free’
order by p2, p1raw;

sid p1raw             p2  p3 seconds_in_wait  wait_time state
---- ---------------- --- --- --------------- ---------- ------------------
  38 00000400837d7800  98   1               1          2 waited known time
  42 00000400837d7800  98   1               1          2 waited known time
  44 00000400837d7800  98   3               1          4 waited known time
  58 00000400837d7800  98   2               1         10 waited known time
  85 00000400837d7800  98   3               1         12 waited known time
 214 00000400837d7800  98   1               1          2 waited known time
 186 00000400837d7800  98   3               1         14 waited known time
 149 00000400837d7800  98   2               1          3 waited known time
 132 00000400837d7800  98   2               1          2 waited known time
 101 00000400837d7800  98   3               1          4 waited known time
 222 00000400837d7800  98   3               1         12 waited known time
 229 00000400837d7800  98   3               1          4 waited known time
 230 00000400837d7800  98   3               1         11 waited known time
 232 00000400837d7800  98   1               1         20 waited known time
 257 00000400837d7800  98   3               1         16 waited known time
 263 00000400837d7800  98   3               1          5 waited known time
 117 00000400837d7800  98   4               1          4 waited known time
 102 00000400837d7800  98   3               1         12 waited known time
  47 00000400837d7800  98   3               1         11 waited known time
  49 00000400837d7800  98   1               1          2 waited known time
  99 00000400837d9300  98   1               1         32 waited known time
  51 00000400837dd200  98   1               1          1 waited known time
  43 00000400837de400  98   1               1          2 waited known time
 130 00000400837de400  98   1               1         10 waited known time
  89 00000400837de400  98   1               1          2 waited known time
  62 00000400837de400  98   0               1         -1 waited known time
 150 00000400837de400  98   1               1          9 waited known time
 195 00000400837de400  98   1               1          3 waited known time
  67 00000400837de400  98   1               1          2 waited known time

 

下一步,就是找出這些熱點塊以及造成latch爭用的sql語句。這是因為cache buffers chains latch通常保護很多個塊,這些熱點塊可能屬於這些sql中使用的某個表。從oracle8i開始,你可以通過接觸點計數(tchtouch count)來發現熱點塊。一般來說,熱點塊的tch會比較高。但是要記住,當塊從lru列表的冷端移動到熱端後,tch會被清0。所以, tch0的塊不一定就不是熱點塊。

-- 這裡使用了前面例子中的p1raw (00000400837d7800).
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 = ’00000400837d7800’
union
select hladdr, file#, dbablk, tch, obj, null
from   x$bh
where  obj in (select obj from x$bh where hladdr = ’00000400837d7800’
               minus
               select object_id from dba_objects
               minus
               select data_object_id from dba_objects)
and    hladdr = ’00000400837d7800’
order by 4;

hladdr           file#  dbablk  tch         obj object_name
---------------- ----- ------- ---- ----------- --------------------
00000400837d7800    16  105132    0       19139 route_history
00000400837d7800    16  106156    0       19163 telco_orders
00000400837d7800    26   98877    0       23346 t1
00000400837d7800    16   61100    0       19163 telco_orders
00000400837d7800    16   26284    0       19059 fp_eq_tasks
00000400837d7800     7  144470    0       18892 report_process_queue
00000400837d7800     8  145781    0       18854 pa_equipment_union
00000400837d7800   249  244085    0       4294967295
00000400837d7800     7   31823    1       18719 candidate_events
00000400837d7800    13  100154    1       19251 event
00000400837d7800     7   25679    1       18730 candidate_zoning
00000400837d7800     7    8271    1       18719 candidate_events
00000400837d7800     7   32847    2       18719 candidate_events
00000400837d7800     8   49518    2       18719 candidate_events
00000400837d7800     7   85071    2       18719 candidate_events
00000400837d7800   275   76948    2       4294967295
00000400837d7800     7   41039    3       18719 candidate_events
00000400837d7800     7   37967    4       18719 candidate_events
00000400837d7800     8   67950    4       18719 candidate_events
00000400837d7800     7   33871    7       18719 candidate_events
00000400837d7800     7   59471    7       18719 candidate_events
00000400837d7800     8    8558   24       18719 candidate_events

 

如前所述,熱點塊通常是應用程式導致的。找出這些程式,檢查他們為什麼重複訪問相同的塊,並且做出相應的調整。

 

另外一個解決辦法,就是儘量將熱點塊分配到不同的hash chain連結串列,由不同的cache buffers chains latch來保護。這可以通過調整熱點塊中的行資料分佈到不同的塊中來實現。新的塊有不同的塊地址,這樣原來在同一個hash chain上的資料就可能會分佈到其他不同的hash chain上。改變塊中行資料的分佈有很多方法,包括:

n         通過rowid刪除並且重新插入某些行。

n         將表exp出來,加大 pctfree,然後再imp表。這樣會使每個塊中的資料減少,使資料分佈到更多的塊上。同時,也會導致佔用更多的空間,全表掃描的效能也會受到影響。

n         儘量減每個塊中的記錄數。首先需要dump一些資料塊來分析現在每個塊中的記錄數。Dump出來的跟蹤檔案中,nrow就是塊中的記錄總數。然後exp表,再truncate表,在表中插入你想要在每個塊中儲存的條數的記錄,然後使用alter table table_name minimize records_per_block,再truncate表,最後imp回資料即可。

n         可以考慮減少塊的大小。從oracle9i開始,資料庫可以支援不同的塊大小。例如當前塊大小為16k,你可以將表及其索引移動塊大小為8k的表空間中。這也會對全表掃描造成負面影響。並且,多個塊大小也會使得管理更復雜。

 

另外,從oracle9iR2開始,也可以通過增加隱含引數_spin_count的值來解決熱點塊導致的cache buffers chains latch爭用。最後,也可以通過隱含引數_db_block_hash_buckets來增加hash bucket的數量,從oracle8i開始,一般不建議採用這種辦法,如果實在要用,請保證_db_block_hash_buckets的值為一個質數,否則,oracle也會自動採用大於你提供的值的最小的質數值。

 

Cache buffers chains latch爭用原因三 ―― 過長的hash chain

多個資料塊可能分配到同一個hash bucket上。這些塊組成一個連結串列(hash chain)。在一個大型系統中,一個hash bucket中可能有上百個資料塊。從一個hash chain連結串列中搜尋某個塊,需要獲得cache buffers chains latch,序列的進行。如果連結串列太長,使得latch持有時間相應增加,可能導致其他程式請求cache buffers chains latch失敗。

 

oracle8.0之前,由於hash latchhash buckethash chain之間是1:1:1的關係,很容易計算一個hash chain的長度,等於一個latch需要保護的資料塊數。通過下面的查詢可以知道一個hash chain上的資料塊數。一般而言,一個hash chain連結串列上超過10個資料塊就認為太長了。

Select hladdr, count(*)
from   x$bh
group by hladdr
order by 2;

 

oracle8i起,hash latchhash bucket之間的關係變成了1:m。這樣就很難計算某個hash chain具體的長度了。只能計算一個hash latch需要保護多少個資料塊。而一個hash latch可能同時保護多個hash chain連結串列。上面的那個查詢的結果變成了每個hash latch需要保護的資料塊數。在你判斷一個hash latch保護的資料塊是否過量之前,需要先得到hash latchhash bucket的比值。在下面的例子中,每個hash latch保護125hash chain。如果你想要每個hash chain上不超過10個資料塊,則每個hash latch保護的資料塊不能超過1250個。通過隱含引數_db_block_hash_buckets可以增加hash bucket的數目,這樣可以減少每個hash chain上的資料塊數(因為hash buckethash chain之間是1:1的關係)。從oracle8i開始,一般不建議這麼做。

_db_block_hash_buckets = 128021
_db_block_hash_latches = 1024
ratio = 128021 / 1024 = 125

 

Cache buffers lru chain latch

除了hash chain,緩衝頭同樣組成一個列表,這個列表指向其他的列表比如lrulruwckpt-qLrulruw列表並不是什麼新東西,他們是資料緩衝區中最早的兩個連結串列。Lru列表包含了不同狀態的快取塊,而lruw就是俗稱的“髒表”,只包含髒資料塊。Lrulruw列表是互斥的,他們合稱一個工作集(a working set)。每個工作集由一個cache buffers lru chain latch保護。換句話說,資料緩衝區中工作集的個數是由cache buffers lru chain latch的個數決定的。通過內部檢視x$kcbwds (kernel cache buffer working sets descriptors)可以知道工作集的個數。我們注意到x$kcbwds set_latc的值就是v$latch_childrenaddr列。

                               lru + lruw = a working set

Select set_id, set_latch 
from   x$kcbwds
order by set_id;

    set_id set_latc
  ---------- --------
         1 247e299c
         2 247e2e68
         3 247e3334
         4 247e3800
         5 247e3ccc
         6 247e4198
         7 247e4664
         8 247e4b30

select addr
from   v$latch_children
where name = ’cache buffers lru chain’
order by addr;

addr
--------
247e299c
247e2e68
247e3334
247e3800
247e3ccc
247e4198
247e4664
247e4b30

 

一般來講,當程式需要查詢可用的快取空間時,需要訪問lru列表。後臺程式DBWn則會將lruw列表中的乾淨塊移到lru列表中,也會將lru中的髒塊移到lruw列表中。在一個工作集中進行以上的任何操作都需要先獲得cache buffers lru chain latch

 

各個資料緩衝區中(包括不同塊大小的緩衝區,keep池和recycle池),每個緩衝區至少需要有一個cache buffers lru chain latch,而一個DBWn程式可能需要多個latch。否則,一個工作集就可能變得很長。在oracle9ioracle10g中,cache buffers lru chain latch的個數預設是cpu個數的4倍,如果,db_writer_processes大於4,則等於cpu的個數乘以db_writer_processes。可以通過隱含引數_db_block_lru_latches來調節cache buffers lru chain latch的個數。

 

Cache buffers laru cahin latch的爭用,主要表現為由於低效的sql語句導致資料緩衝區過度活躍。全表掃描和對某些選擇性較差的大索引的反覆掃描是造成cache buffers laru cahin latch爭用的主要原因。解決辦法是,查詢latch free等待事件中關於cache buffers lru chain latch相關的sql語句(在oracle10g中,已經變成一個獨立的cache buffers lru chain等待事件),優化這些sql,降低其物理讀和邏輯讀。

 

Row cache objects latch

Row cache objects latch用來保護資料字典緩衝區(row cache的名字主要是因為其中的資訊是按行儲存的,而不是按塊儲存)。程式在裝載、引用或者清除資料字典緩衝區中的物件時必須獲得該latch。在oracle8i之前,這是一個獨立latch。從oracle9i起,由於引入了多個子共享池的新特性,存在多個row cache objectslatchOracle10g中,該latch也有了一個獨立的等待事件:row cache objects

 

oracle7.0起,資料字典緩衝成為了共享池的一部分。而在7.0之前,每個資料字典物件都是由獨立的dc_*初始化引數控制。Oracle7.0的這個改變也意味著,不能再直接的調整資料字典緩衝,而只能通過調整shared_pool_size來間接的調整。V$rowcache檢視包含了每個資料字典物件的統計資訊。你可以通過下面的查詢發現最熱的資料字典物件。

Select cache#, type, parameter, gets, getmisses, modifications mod 
from   v$rowcache
where  gets > 0
order by gets;

cache# type        parameter                gets  getmisses    mod
------ ----------- ------------------ ---------- ---------- ------
     7 subordinate dc_user_grants        1615488         75      0
     2 parent      dc_sequences          2119254     189754    100
    15 parent      dc_database_links     2268663          2      0
    10 parent      dc_usernames          7702353         46      0
     8 parent      dc_objects           11280602      12719    400
     7 parent      dc_users             81128420         78      0
    16 parent      dc_histogram_defs   182648396      51537      0
    11 parent      dc_object_ids       250841842       3939     75

 

對資料字典緩衝區的調節手段是有限的。最好的辦法是降低對前面的查詢結果中一些熱點資料字典物件的訪問。舉個例子,如果對某些sequence訪問頻繁,可以將考慮將這些sequnce快取在記憶體中。包含多個基表連線或者基於檢視的檢視可能導致該latch爭用。一般的解決辦法是增加shared_pool_size的值。

 

 

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

相關文章