引言
Oracle Rdbms應用了各種不同類型的鎖定機制,latch即是其中的一種,本文將集中介紹latch(閂)的概念,理解latch的實現方法並說明引起latch衝突的原因。
什麼是latch
Latch是用於保護SGA區中共用資料結構的一種串列化鎖定機制。Latch的實現是與作業系統相關的,尤其和一個進程是否需要等待一個latch、需要等待多長時間有關。
Latch是一種能夠極快地被獲取和釋放的鎖,它通常用於保護描述buffer cache中block的資料結構。與每個latch相聯繫的還有一個清除過程,當持有latch的進程成為死進程時,該清除過程就會被調用。Latch 還具有相關級別,用於防止鎖死,一旦一個進程在某個級別上得到一個latch,它就不可能再獲得等同或低於該級別的latch。
Latch與Enqueue(佇列)
Enqueue是Oracle使用的另一種鎖定機制,它更加複雜,允許幾個併發進程不同程度地共用某些資源。任何可被併發使用的物件均可使用 enqueue加以保護。一個典型的例子是表的鎖定,我們允許在一個表上有不同級別的共用。與latch不同之處在於,enqueue是使用作業系統特定 的鎖定機制,一個enqueue允許用戶在鎖上存貯一個標記,來表明請求鎖的模式。作業系統lock manager跟蹤所有被鎖定的資源,如果某個進程不能獲取它所請求的那種鎖,作業系統就把請求進程置於一個等待佇列中,該佇列按FIFO原則調度,而在 latches中是沒有象enqueue中排序的等待佇列,latch等待進程要麼使用計時器來喚醒和重試,要麼spin(只用於多處理器情況下)。
何時需要latch
當一個進程準備訪問SGA中的資料結構時,它就需要獲得一個latch。當進程獲得latch後,它將一直持有該latch直到它不再使用此資料結構,這時latch才會被釋放。可通過latch名稱來區分它所保護的不同資料結構。
Oracle使用元指令對latch進行操作, 當所需的latch已被其他進程持有時,執行指令進程將停止執行部分指令,直到該latch被釋放為止。從根本上講,latch防止併發訪問共用資料結 構,由於設置和釋放latch的指令是不可分割的,作業系統就可以保證只有一個進程獲得latch,又由於這只是單條指令,所以執行速度很快。latch 被持有的時間是很短,而且提供了當持有者不正常中斷時的清除機制,該清除工作是由Oracle後臺進程PMON來完成的。
什麼導致latch衝突
Latch保護SGA中的資料結構被多個用戶同時訪問,如果一個進程不能立即得到所需latch,它就必須等待,這就導致了CPU的額外負擔和系統的速度 降低。額外的CPU使用是進程‘spining’導致的,‘spining’是指進程定時地重複嘗試獲取latch,在連續兩次之間,進程處於休眠狀態, 在得到latch之前,spining過程將重複進行下去。
如何標識內部latch的衝突
Server manager monitor是一個相當有用的來監視latch等待、請求和衝突的工具。也可查詢相關的資料字典表:v$latch, v$latchholder, v$latchname。
v$latch表的每一行包括了對不同類型latch的統計,每一列反映了不同類型的latch請求的活動情況。不同類型的latch請求之間的區別在 於,當latch不可立即獲得時,請求進程是否繼續進行。按此分類,latch請求的類型可分為兩類:willing-to-wait和 immediate。
Willing-to-wait : 是指如果所請求的latch不能立即得到,請求進程將等待一很短的時間後再次發出請求。進程一直重複此過程直到得到latch。
Immediate:是指如果所請求的latch不能立即得到,請求進程就不再等待,而是繼續執行下去。
在v$latch中的以下欄位反映了Willing-to-wait請求:
GETS---成功地以Willing-to-wait請求類型請求一個latch的次數。
MISSES---初始以Willing-to-wait請求類型請求一個latch不成功的次數。
SLEEPS---初始以Willing-to-wait請求類型請求一個latch不成功後,進程等待獲取latch的次數。
在v$latch中的以下欄位反映了Immediate類請求:
IMMEDIATE_GETS---以Immediate請求類型成功地獲得一個latch的次數。
IMMEDIATE_MISSES---以Immediate請求類型請求一個latch不成功的次數。
我們可以通過對v$latch, v$latchholder, v$latchname的查詢獲得有關latch資訊,例如:
/* 已知一個latch位址,找到latch名字 */
col name for a40
select a.name from v$latchname a, v$latch b
where b.addr = '&addr'
and b.latch#=a.latch#;
/* 顯示系統範圍內的latch統計 */
column name format A32 truncate heading "LATCH NAME"
column pid heading "HOLDER PID"
select c.name,a.addr,a.gets,a.misses,a.sleeps,
a.immediate_gets,a.immediate_misses,b.pid
from v$latch a, v$latchholder b, v$latchname c
where a.addr = b.laddr(+)
and a.latch# = c.latch#
order by a.latch#;
/* 由latch名稱顯示對latch的統計 */
select c.name,a.addr,a.gets,a.misses,a.sleeps,
a.immediate_gets,a.immediate_misses,b.pid
from v$latch a, v$latchholder b, v$latchname c
wher
e a.addr = b.laddr(+) and a.latch# = c.latch#
and c.name like '&latch_name%' order by a.latch#;
latch有40餘種,但作為DBA關心的主要應有以下幾種:
Cache buffers chains latch: 當用戶進程搜索SGA尋找database cache buffers時需要使用此latch。
Cache buffers LRU chain latch: 當用戶進程要搜索buffer cache中包括所有 dirty blocks的LRU (least recently used) 鏈時使用該種latch。
Redo log buffer latch: 這種latch控制redo log buffer中每條redo entries的空間分配。
Row cache objects latch: 當用戶進程訪問緩存的資料字典數值時,將使用Row cache objects latch。
下面我們將著重介紹一下如何檢測和減少redo log buffer latch的衝突。對redo log buffer的訪問是由redo log buffer latch來控制的,這種latch有兩種類型, redo allocation latch和redo copy latch。
Redo allocation latch控制redo entries在redo log buffer中的空間分配。Oracle的一個用戶進程只有得到redo allocation latch後才能為redo entries在redo log buffer中分配空間,又由於一個instance只有一個redo allocation latch,所以一次只有一個用戶進程在buffer中分配空間。當用戶進程獲得latch後,首先為redo entry分配空間,然後進程繼續持有latch並拷貝entry到buffer中,這種拷貝稱為“在redo allocation latch上的拷貝”(copying on the redo allocation latch),拷貝完畢後,用戶進程釋放該latch。
一個“在redo allocation latch上的拷貝”的redo entry的最大值是由初始化參數LOG_SMALL_ENTRY_MAX_SIZE定義的,根據作業系統的不同而不同。
Redo Copy Latch只應用於多CPU的系統。在多CPU的instance中,如果一個redo entry太大,超過了LOG_SMALL_ENTRY_MAX_SIZE定義值,則不能進行“在redo allocation latch上的拷貝”, 此時用戶進程必須獲取redo copy latch。一個instance中可以有多個redo copy latch,其數目由初始參數LOG_SIMULTANEOUS_COPIES決定,缺省值為CPU數目。
在單CPU情況下,不存在redo copy latch,所有的redo entry無論大小, 都進行“在redo allocation latch上的拷貝”。
對redo log buffer的過多訪問將導致redo log buffer latch的衝突,latch衝突將降低系統性能,我們可通過如下查詢來檢測這種latch衝突:
col name for a40
SELECT ln.name,gets,misses,immediate_gets,immediate_misses
FROM v$latch l,v$latchname ln
WHERE ln.name IN('redo allocation','redo copy') AND ln.latch#=l.latch#
/
若misses與gets的比例超過1%或immediate_misses與(immediate_gets+immediate_misses)比例超過1%時,應考慮採取措施減少latch的衝突。
大多數的redo log buffer latch衝突是在多個CPU情況下,兩個或多個Oracle進程試圖同時得到相同的latch發生的。由於一個instance只有一個redo allocation latch,為減少redo allocation latch的衝突,應減少單個進程持有latch的時間,這可以通過減小初始參數LOG_SMALL_ENTRY_MAX_SIZE以減小redo entry的數目和大小來實現。如果觀察到有redo copy latch衝突,可以通過增大LOG_SIMULTANEOUS_COPIES 初始參數來加大latch數目,其缺省值為CPU數目,最大可增大到CPU數目的兩倍。