一. Latch 說明
1.1 Latch
Latch屬於System Lock, 用於保護SGA區中共用資料結構的一種序列化鎖定機制。Latch的實現是與作業系統相關的,尤其和一個進程是否需要等待一個latch、需要等待多長時間有關。
Latch是Oracle提供的輕量級鎖資源,是一種能夠極快地被擷取和釋放的鎖,能快速,短時間的鎖定資源,防止多個並發進程同時修改訪問某個共用資源,它只工作在SGA中,通常用於保護描述buffer cache中block的資料結構。
比如SGA中,各種資料被反覆從磁碟讀取到記憶體,又被重新寫回到磁碟上,如果有並發的使用者做相同的事情,Oracle必須使用一種機制,來保證資料在讀取的時候,只能由一個會話來完成,這種保護機制就是Latch。
並發(concurrency):是說有超過兩個以上的使用者對同樣的資料做修改(可能包括插入,刪除和修改)。
並行(parallel):是說將一件事情分成很多小部分,讓每一部分同時執行,最後將執行結果匯總成最終結果。
與每個latch相聯絡的還有一個清除過程,當持有latch的進程成為死進程時,該清除過程就會被調用。Latch還具有相關層級,用於防止死結,一旦一個進程在某個層級上得到一個latch,它就不可能再獲得等同或低於該層級的latch。
Latch不會造成阻塞,只會導致等待。 阻塞是一種系統設計上的問題,等待是一種系統資源爭用的問題。
1.2 有關SPin 的說明:
比如資料緩衝中的某個塊要被讀取,我們會獲得這個塊的 latch,這個過程叫做spin,另外一個進程恰好要修改這個塊,他也要spin這個塊,此時他必須等待,當前一個進程釋放latch後才能spin住,然後修改,如果多個進程同時請求的話,他們之間將出現競爭,沒有一個入隊機制,一旦前面進程釋放所定,後面的進程就蜂擁而上,沒有先來後到的概念,並且這一切都發生的非常快,因為Latch的特點是快而短暫。
SPIN 與 休眠:
休眠意味著暫時的放棄CPU,進行環境切換(context switch),這樣CPU要儲存當前進程運行時的一些狀態資訊,比如堆棧,訊號量等資料結構,然後引入後續進程的狀態資訊,處理完後再切換回原來的進程狀態,這個過程如果頻繁的發生在一個高事務,高並發進程的處理系統裡面,將是個很昂貴的資源消耗,所以Oracle選擇了spin,讓進程繼續佔有CPU,運行一些空指令,之後繼續請求,繼續spin,直到達到_spin_count值,這時會放棄CPU,進行短暫的休眠,再繼續剛才的動作。
1.3 進程擷取Latch的過程:
任何時候,只有一個進程可以訪問記憶體中的某一個資料區塊,如果進程因為別的進程正佔用塊而無法獲得Latch時,他會對CPU進行一次spin(旋轉),時間非常的短暫,spin過後繼續擷取,不成功仍然spin,直到 spin次數到達閥值限制(這個由隱含參數_spin_count指定),此時進程會停止spin,進行短期的休眠,休眠過後會繼續剛才的動作,直到擷取塊上的Latch為止。
進程休眠的時間也是存在演算法的,他會隨著spin次數而遞增,以厘秒為單位,如1,1,2,2,4,4,8,8,。。。休眠的閥值限制由隱含參數_max_exponential_sleep控制,預設是2秒,如果當前進程已經佔用了別的Latch,則他的休眠時間不會太長(過長會引起別的進程的Latch等待),此時的休眠最大時間有隱含參數_max_sleep_holding_latch決定,預設是4厘秒。這種時間限制的休眠又稱為短期等待。
另外一種情況是長期等待鎖存器(Latch Wait Posting),此時等待進程請求Latch不成功,進入休眠,他會向鎖存器等待鏈表(Latch Wait List)壓入一條訊號,表示擷取Latch的請求,當佔用進程釋放Latch時會檢查Latch Wait List,向請求的進程傳遞一個訊號,啟用休眠的進程。Latch Wait List是在SGA區維護的一個進程列表,他也需要Latch來保證其正常運行,預設情況下share pool latch和library cache
latch是採用這個機制。
如果將隱含參數_latch_wait_posting設定為2,則所有Latch都採用這種等待方式,使用這種方式能夠比較精確的喚醒某個等待的進程,但維護Latch Wait List需要系統資源,並且對Latch Wait List上Latch的競爭也可能出現瓶頸。
如果一個進程請求,旋轉,休眠Latch用了很長時間,他會通知PMON進程,查看Latch的佔用進程是否已經意外終止或死亡,如果是則PMON會清除釋放佔用的Latch資源。
總之, Latch擷取的流程:請求-SPIN-休眠-請求-SPIN-休眠 ... ... 佔用。
1.4 Latch 和 Lock
從某種意義上說,Latch是記憶體中的資源鎖,資料庫物件(表,索引等)的鎖叫Lock。
Latch和Lock的區別:
(1). Latch是對記憶體資料結構提供互斥訪問的一種機制,而Lock是以不同的模式來套取共用資源對象,各個模式間存在著相容或排斥,從這點看出,Latch 的訪問,包括查詢也是互斥的,任何時候,只能有一個進程能pin住記憶體的某一塊,幸好這個過程是相當的短暫,否則系統效能將沒的保障,從9I開始,允許多個進程同時查詢相同的記憶體塊。
(2). Latch只作用於記憶體中,他只能被當前執行個體訪問,而Lock作用於資料庫物件,在RAC體系中執行個體間允許Lock檢測與訪問
(3). Latch是瞬間的佔用,釋放,Lock的釋放需要等到事務正確的結束,他佔用的時間長短由事務大小決定
(4). Latch是非入隊的,而Lock是入隊的
(5). Latch不存在死結,而Lock中存在。
二. Latch 爭用
如果發現系統中經常由於Lock 導致使用者等待,這時需要考慮系統在邏輯設計上是否有問題,比如多使用者對主鍵的刪除或者修改,是否有使用者使用select … for update這樣的文法,外鍵是否建立索引的因素。 這些因素是需要結合系統的商務邏輯性來進行資料庫物件設計的。
如果發現系統慢是因為很多的Latch爭用,就要考慮系統及資料庫自身設計上是否存在問題,比如是否使用綁定變數,是否存在熱快,資料存放區參數設計是否合理等因素。
導致Latch爭用而等待的原因非常多,記憶體中很多資源都可能存在爭用。 最常見的兩類latch爭用如下:
(1)共用池中的Latch爭用。
(2)資料緩衝池中的latch爭用。
2.1 共用池中的Latch爭用
共用池中如果存在大量的SQL被反覆分析,就會造成很大的Latch爭用和長時間的等待,最常見的現象就是沒有綁定變數。
最常見的集中共用池裡的Latch是 library cache。 可以通過一下SQL 來查詢:
SQL> select * from v$latchname where name like 'library cache%';
LATCH# NAME HASH
---------- -------------------------------------------------- ----------
217 library cache 3055961779
218 library cache lock 916468430
219 library cache pin 2802704141
220 library cache pin allocation 4107073322
221 library cache lock allocation 3971284477
222 library cache load lock 2952162927
223 library cache hash chains 1130479025
在分析系統效能時,如果看到有library cache 這樣的Latch爭用,就可以斷定是共用池中出現了問題,這種問題基本是由SQL語句導致的,比如沒有綁定變數或者一些預存程序被反覆分析。
資源的爭用可以通過如下SQL 來查看:
SQL> select event,count(*) from v$session_wait group by event;
EVENT COUNT(*)
---------------------------------------------------------------- ----------
SQL*Ne tmessage from client 4
Streams AQ: waiting for messages in the queue 1
ASM background timer 1
gcs remote message 1
ges remote message 1
jobq slave wait 1
rdbms ipc message 14
smon timer 1
pmon timer 1
Streams AQ: qmn slave idle wait 1
class slave wait 1
SQL*Net message to client 1
Streams AQ: waiting for time management or cleanup tasks 1
Streams AQ: qmn coordinator idle wait 1
DIAG idle wait 1
15 rows selected.
2.2 資料緩衝池Latch爭用
訪問頻率非常高的資料區塊被稱為熱快(Hot Block),當很多使用者一起去訪問某幾個資料區塊時,就會導致一些Latch爭用,最常見的latch爭用有:
(1) buffer busy waits
(2) cache buffer chain
這兩個Latch的爭用分別發生在訪問資料區塊的不同時刻。
Cache buffer chian產生原因:
當一個會話需要去訪問一個記憶體塊時,它首先要去一個像鏈表一樣的結構中去搜尋這個資料區塊是否在記憶體中,當會話訪問這個鏈表的時候需要獲得一個Latch,如果擷取失敗,將會產生Latch cache buffer chain 等待,導致這個等待的原因是訪問相同的資料區塊的會話太多或者這個列表太長(如果讀到記憶體中的資料太多,需要管理資料區塊的hash列表就會很長,這樣會話掃描列表的時間就會增加,持有chache buffer chain latch的時間就會變長,其他會話獲得這個Latch的機會就會降低,等待就會增加)。
Buffer busy waits 產生原因:
當一個會話需要訪問一個資料區塊,而這個資料區塊正在被另一個使用者從磁碟讀取到記憶體中或者這個資料區塊正在被另一個會話修改時,當前的會話就需要等待,就會產生一個buffer busy waits等待。
產生這些Latch爭用的直接原因是太多的會話去訪問相同的資料區塊導致熱快問題,造成熱快的原因可能是資料庫設定導致或者重複執行的SQL 頻繁訪問一些相同的資料區塊導致。
Latch是簡單的、低層次的序列化技術,用以保護SGA中的共用資料結構,比如並發使用者列表和buffer cache裡的blocks資訊。一個伺服器處理序或後台進程在開始操作或尋找一個共用資料結構之前必須獲得對應的latch,在完成以後釋放latch。不必對latch本身進行最佳化,如果latch存在競爭,表明SGA的一部分正在經曆不正常的資源使用
三. 檢查Latch 的相關SQL
3.1 查看造成LATCH BUFFER CACHE CHAINS等待事件的熱快
SELECT DISTINCT a.owner, a.segment_name
FROM dba_extents a,
(SELECT dbarfil, dbablk
FROM x$bh
WHERE hladdr IN (SELECT addr
FROM ( SELECT addr
FROM v$latch_children
ORDER BY sleeps DESC)
WHERE ROWNUM < 20)) b
WHERE a.RELATIVE_FNO = b.dbarfil
AND a.BLOCK_ID <= b.dbablk
AND a.block_id + a.blocks > b.dbablk;
3.2 查詢當前資料庫最繁忙的Buffer,TCH(Touch)表示訪問次數越高,熱點快競爭問題就存在
SELECT *
FROM ( SELECT addr,
ts#,
file#,
dbarfil,
dbablk,
tch
FROM x$bh
ORDER BY tch DESC)
WHERE ROWNUM < 11;
3.3 查詢當前資料庫最繁忙的Buffer,結合dba_extents查詢得到這些熱點Buffer來自哪些對象
SELECT e.owner, e.segment_name, e.segment_type
FROM dba_extents e,
(SELECT *
FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch
FROM x$bh
ORDER BY tch DESC)
WHERE ROWNUM < 11) b
WHERE e.relative_fno = b.dbarfil
AND e.block_id <= b.dbablk
AND e.block_id + e.blocks > b.dbablk;
3.4 如果在Top 5中發現latch free熱點塊事件時,可以從V$latch_children中查詢具體的子Latch資訊
SELECT *
FROM (SELECT addr, child#, gets, misses, sleeps, immediate_gets igets,
immediate_misses imiss, spin_gets sgets
FROM v$latch_children
WHERE NAME = 'cache buffers chains'
ORDER BY sleeps DESC)
WHERE ROWNUM < 11;
3.5 擷取當前持有最熱點資料區塊的Latch和buffer資訊
SELECT b.addr, a.ts#, a.dbarfil, a.dbablk, a.tch, b.gets, b.misses, b.sleeps
FROM (SELECT *
FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch, hladdr
FROM x$bh
ORDER BY tch DESC)
WHERE ROWNUM < 11) a,
(SELECT addr, gets, misses, sleeps
FROM v$latch_children
WHERE NAME = 'cache buffers chains') b
WHERE a.hladdr = b.addr;
3.6 利用前面的SQL可以找到這些熱點Buffer的對象資訊
SELECT distinct e.owner, e.segment_name, e.segment_type
FROM dba_extents e,
(SELECT *
FROM (SELECT addr, ts#, file#, dbarfil, dbablk, tch
FROM x$bh
ORDER BY tch DESC)
WHERE ROWNUM < 11) b
WHERE e.relative_fno = b.dbarfil
AND e.block_id <= b.dbablk
AND e.block_id + e.blocks > b.dbablk;
3.7 結合SQL視圖可以找到操作這些對象的相關SQL,然後通過最佳化SQL減少資料的訪問,或者最佳化某些容易引起爭用的操作(如connect by等操作)來減少熱點塊競爭
break on hash_value skip 1
SELECT /*+ rule */ hash_value,sql_text
FROM v$sqltext
WHERE (hash_value, address) IN (
SELECT a.hash_value, a.address
FROM v$sqltext a,
(SELECT DISTINCT a.owner, a.segment_name, a.segment_type
FROM dba_extents a,
(SELECT dbarfil, dbablk
FROM (SELECT dbarfil, dbablk
FROM x$bh
ORDER BY tch DESC)
WHERE ROWNUM < 11) b
WHERE a.relative_fno = b.dbarfil
AND a.block_id <= b.dbablk
AND a.block_id + a.blocks > b.dbablk) b
WHERE a.sql_text LIKE '%' || b.segment_name || '%'
AND b.segment_type = 'TABLE')
ORDER BY hash_value, address, piece;