天天看點

Oracle Latch 管理

資料庫系統本身是一個多使用者并發處理系統,在同一個時間點上,可能會有多個使用者同時操作資料庫。

這裡就涉及兩個很重要的問題。

這些使用者之間的操作不會互相破壞。比如兩個使用者同時在相同的實體位置上寫資料時,不能發生互相覆寫的情況。這叫串行化,也就是說,即便兩個使用者同時寫,也必須有先後,一個使用者寫完,另一個使用者繼續寫。串行化會降低系統的并發性,但這對于保護資料結構不被破壞來說則是必需的。

在滿足串行化的前提下,如何将并發性提升到最大。

在Oracle資料庫中,通過闩鎖(latch)和鎖定(lock)來解決這兩個問題。闩鎖和鎖定既有相同點又有不同點。相同點在于它們都是用于實作串行化的資源。而不同點則在于闩鎖是一個低級别、輕量級的鎖,獲得和釋放的速度很快,以類似于信号燈的方式實作。而鎖定則可能持續的時間很長,通過使用隊列,按照先進先出的方式實作。也可以簡單地了解為闩鎖是微觀領域的,而鎖定則是宏觀領域的。

闩鎖是什麼,以及latch是如何保護資源的;

鎖定(包括TX鎖和TM鎖)是如何保護資源的;

如何檢測并解決鎖定沖突;

DDL鎖定的概念;

如何建立我們自己的鎖定。

10.1 闩鎖(latch)概述

Oracle資料庫使用闩鎖來管理記憶體的配置設定和釋放。假設,某個使用者程序(假設其為A)發出一條update語句,要去更新58号資料塊裡的某條記錄。則該使用者程序對應的伺服器程序在寫記憶體的時候,找到58号資料塊,并往裡寫内容。A在寫58号資料塊的過程中,這時,另一個使用者程序B發出

insert語句,要将某個新的記錄插入到58号資料塊裡。如果沒有一定的保護機制,A正要寫入的空間可能會被B搶先寫入,或者相反,B正要寫入的空間也可能會被A搶先寫入。不管哪個使用者先搶先寫入,造成的結果就是,58号資料塊裡的資料都混亂了,因為這時,A和B之間的資料互相交織在一起了。

是以,必須使用latch對此進行保護。簡單來說,任何程序要寫資料塊時,都必須先獲得latch,在寫入過程中,一直持有該latch,寫完以後,釋放該latch。對于上面的例子來說,當A在寫入58号資料塊時,先獲得latch,然後開始寫。而當A正在寫入的過程中,B也要寫58号資料塊。這時B在嘗試獲得latch時,發現該latch正被其他使用者(也就是A)持有,是以B進入等待狀态。直到A寫完資料塊并釋放latch以後,B才能獲得

latch,獲得latch以後,才能在58号資料塊裡寫入資料。

這裡隻是以寫資料塊為例來說明為何要使用latch。而事實上,latch不僅僅用于寫資料塊,比如對于shared

pool來說,其記憶體機關就不是資料塊了。latch也不僅僅用于寫操作,隻要涉及記憶體位址的讀和寫,都需要通過獲得latch來實作串行化,一次隻能有一個伺服器程序在讀或者寫記憶體位址。

Oracle在執行個體管理中,不管是buffer cache、shared pool還是log buffer,都引入了各種各樣的latch。

實作latch時,實際是由作業系統的旗語(semaphore:也叫信号量)來完成的。為了便于了解,可以把它們想象為,通過某個變量值的變化而實作的。變量值為0則說明latch目前沒有被其他程序擷取,否則如果為非0值,則說明它已經被其他程序所擷取了。Oracle在設計latch的時候将其定義為輕量級鎖,是以它的操作非常快,以微秒(microsecond,也就是百萬分之一秒)來計算。

latch分為兩種類型:

1. 願意等待(Willing-To-Wait)

大部分的latch都屬于這種類型。這種類型的latch都是通過Test-And-Set的方式來實作的。也就是說,如果目前程序不能獲得

latch的時候,會繞着CPU旋轉,而不放棄CPU。這也就是所謂的SPIN

CPU,實際就是執行一段空循環,類似執行下面一段代碼(其中的N由Oracle内部來控制):

loop

exit when i>= N

i := i+1;

null;

end loop;

程序之是以不釋放CPU而是繞着CPU旋轉,是由于latch操作本身是一個很快速的動作,是以可能等一會就能獲得latch了。當程序一旦獲得

CPU,但是獲得不了latch時,如果這時候立刻放棄CPU,那麼需要進行上下文切換,下次再次嘗試獲得latch時,又要進行上下文切換,可能反而要消耗更多的時間。是以,程序在不能獲得latch的時候,會執行上面這段代碼,繞着CPU轉一會,然後再次嘗試獲得latch,如果仍然不能獲得,則再次旋轉CPU。當反複旋轉CPU并嘗試獲得latch的的次數超過某個上限(該上限由隐藏參數控制)時,這時程序會釋放CPU,并進入睡眠(Sleep)狀态。程序一旦進入睡眠狀态,則會抛出一個對應的等待事件,并記錄在視圖v$session_wait裡,說明目前該程序正在等待的latch的類型等資訊。初始狀态下,一個程序會睡眠0.01秒。然後醒過來,并再次嘗試獲得latch。如果旋轉CPU的次數達到上限以後,仍然不能獲得latch,則再次進入睡眠,這時會睡眠兩倍的時間,依此類推,直到達到睡眠的最大值:0.2秒。

這是在資料庫伺服器具有多個CPU時的情形,如果隻有一個CPU,就不存在旋轉CPU的情況,一旦獲得不了latch,就進入睡眠。

總的來說,當程序嘗試擷取Willing-To-Wait類型的latch時,如果失敗,則程序會一直嘗試對latch的擷取,不斷循環,直到獲得

latch為止,或者是達到所指定的上限值為止。當達到上限值時,程序進入睡眠。

2. 不等待(No-Wait)

這種類型的latch比較少,對于這種類型的latch來說,都會有很多個可用的latch。當一個程序請求其中的一個latch時,會以no-

wait模式開始請求。如果所請求的latch不可用,則程序不會等待,而是立刻請求另外一個latch。隻有當所有的latch都不能獲得時,才會進入等待。

從另外一個角度來說,latch分為單個latch(Solitary latch,比如shared pool latch以及redo

allocation latch等)和latch組(比如library cache latch、cache buffers lru chain

latch以及cache buffers chains

latch等)。latch組包括父latch和子latch。單個latch和父latch都是定義在資料庫軟體代碼裡的,而且都是靜态配置設定的。對于每種類型的latch,隻有一個父latch。而子latch則根據參數或預設值而動态設定,而且子latch的通路獨立于父latch。通常來說,父

latch隻用于彙總顯示報表的目的。

如果latch資源被争用,通常都會表現為CPU資源使用過高。而反過來說,如果我們發現CPU資源很緊張,使用率總是在90%以上,甚至總是在

100%,其主要原因有以下幾點。

SQL語句沒有使用綁定變量。如果沒有使用綁定變量,或者書寫SQL時随意性過大,比如大小寫混用等。則Oracle對每一條SQL語句都要進行解析,也就是要非常頻繁地讀寫shared

pool裡的記憶體塊,進而導緻與解析SQL相關的latch争用。

執行SQL語句時,掃描的資料塊過多,或者說SQL語句寫的比較低效,導緻要掃描很多的資料塊才能傳回所要的記錄。因為在查找、掃描資料塊的過程中,程序也要獲得latch,直到找到資料塊為止。

為何一旦latch資源發生争用,就會導緻CPU繁忙呢?可以想象一下,假設某個程序(A)執行一條SQL語句需要通路10000個資料塊,那麼該程序在掃描資料塊的過程中,一直持有latch。而另一個程序B也要執行SQL,但是由于A持有了latch,導緻B無法獲得,于是旋轉一會CPU,再去獲得latch,直到進入睡眠才釋放CPU。接下來C程序也要執行SQL,同樣的,由于A持有了latch,導緻C無法獲得,于是也旋轉一會CPU,再去獲得latch,直到進入睡眠才釋放CPU。如果類似B和C的程序很多的話,那我們會發現,CPU總是在被旋轉,也就是在做空的循環,而無法做其他的事情。是以,展現出CPU的使用率過高。

要解決latch的争用,關鍵在于共享SQL語句(比如使用綁定變量、規範SQL的書寫等)以及優化SQL語句,使其搜尋以及掃描的資料塊的個數下降到最低。

Oracle

Rdbms應用了各種不同類型的鎖定機制,latch即是其中的一種。Latch是用于保護SGA區中共享資料結構的一種串行化鎖定機制。Latch的實作是與作業系統相關的,尤其和一個程序是否需要等待一個latch、需要等待多長時間有關。Latch是一種能夠極快地被擷取和釋放的鎖,它通常用于保護描述buffer

cache中block的資料結構。與每個latch相聯系的還有一個清除過程,當持有latch的程序成為死程序時,該清除過程就會被調用。Latch還具有相關級别,用于防止死鎖,一旦一個程序在某個級别上得到一個latch,它就不可能再獲得等同或低于該級别的latch。

本視圖儲存自執行個體啟動各類栓鎖的統計資訊。常用于當v$session_wait中發現栓鎖競争時鑒别SGA區中問題所在區域。

v$latch表的每一行包括了對不同類型latch的統計,每一列反映了不同類型的latch請求的活動情況。不同類型的latch請求之間的差別在于,當latch不可立即獲得時,請求程序是否繼續進行。按此分類,latch請求的類型可分為兩類:willing-to-wait和immediate。

Willing-to-wait:是指如果所請求的latch不能立即得到,請求程序将等待一很短的時間後再次送出請求。程序一直重複此過程直到得到latch。

Immediate:是指如果所請求的latch不能立即得到,請求程序就不再等待,而是繼續執行下去。

V$LATCH中的常用列:

NAME:latch名稱

IMMEDIATE_GETS:以Immediate模式latch請求數

IMMEDIATE_MISSES:請求失敗數

GETS:以Willing to wait請求模式latch的請求數

MISSES:初次嘗試請求不成功次數

SPIN_GETS:第一次嘗試失敗,但在以後的輪次中成功

SLEEP[x]:成功擷取前sleeping次數

WAIT_TIME:花費在等待latch的時間

V$LATCH中的連接配接列

Column        View            Joined

Column(s)

NAME/LATCH#        V$LATCH_CHILDREN        NAME/LATCH#

NAME        V$LATCHHOLDER        NAME

NAME/LATCH#        V$LATCHNAME        NAME/LATCH#

NAME        V$LATCH_MISSES        PARENT_NAME

示例:

下列的示例中,建立一個表存儲查詢自v$latch的資料:

SQL> CREATE TABLE snap_latch as SELECT 0 snap_id, sysdate snap_date, a.*

FROM V$LATCH a;

SQL> ALTER TABLE snap_latch add (constraint snap_filestat primary key

(snap_id, name));

最初,snap_id被置為0,稍後,snap_latch表的snap_id列被更新為1:

SQL> INSERT INTO snap_latch SELECT 1, sysdate, a.* FROM V$LATCH

a;

注意你通過sql語句插入記錄時必須增加snap_id的值。

在你連續插入記錄之後,使用下列的select語句列出統計。注意0不能成為被除數。

SQL> SELECT SUBSTR(a.name,1,20) NAME, (a.gets-b.gets)/1000

"Gets(K)",

(a.gets-b.gets)/(86400*(a.snap_date-b.snap_date)) "Get/s",

DECODE ((a.gets-b.gets), 0, 0,

(100*(a.misses-b.misses)/(a.gets-b.gets))) MISS,

DECODE ((a.misses-b.misses), 0, 0,

(100*(a.spin_gets-b.spin_gets)/(a.misses-b.misses))) SPIN,

(a.immediate_gets-b.immediate_gets)/1000

"Iget(K)",

(a.immediate_gets-b.immediate_gets)/

(86400*(a.snap_date-b.snap_date)) "IGet/s",

DECODE ((a.immediate_gets-b.immediate_gets), 0,

0,

(100*(a.immediate_misses-b.immediate_misses)/

(a.immediate_gets-b.immediate_gets))) IMISS

FROM snap_latch a, snap_latch b

WHERE a.name = b.name

AND a.snap_id = b.snap_id + 1

AND ( (a.misses-b.misses) > 0.001*(a.gets-b.gets)

or (a.immediate_misses-b.immediate_misses)

>

0.001*(a.immediate_gets-b.immediate_gets))

ORDER BY 2 DESC;

下例列出latch統計項,miss列小于0.1%的記錄已經被過濾。

NAME                Gets(K)    Get/s MISS    SPIN IGets(K) IGet/s IMISS

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

-----

cache buffers chai 255,272    69,938    0.4    99.9    3,902    1,069    0.0

library cache      229,405    62,851    9.1    96.9    51,653   14,151    3.7

shared pool        24,206    6,632     14.1    72.1         0       0    0.0

latch wait list    1,828        501     0.4    99.9    1,836        503    0.5

row cache objects   1,703       467     0.7    98.9    1,509        413    0.2

redo allocation       984       270     0.2    99.7        0          0    0.0

messages              116        32     0.2    100.0       0          0    0.0

cache buffers lru      91        25     0.3     99.0    7,214     1,976    0.3

modify parameter v      2         0     0.1    100.0        0         0    0.0

redo copy               0         0    92.3     99.3    1,460       400    0.0

ColumnViewJoined

NAME/LATCH#V$LATCH_CHILDRENNAME/LATCH#

NAMEV$LATCHHOLDERNAME

NAME/LATCH#V$LATCHNAMENAME/LATCH#

NAMEV$LATCH_MISSESPARENT_NAME

NAMEGets(K)Get/s MISSSPIN IGets(K) IGet/s IMISS

cache buffers chai 255,272 69,9380.499.93,9021,0690.0

library cache229,405 62,8519.196.951,653 14,1513.7

shared pool24,2066,632 14.172.1000.0

latch wait list1,8285010.499.91,8365030.5

row cache objects1,7034670.798.91,5094130.2

redo allocation9842700.299.7000.0

messages116320.2 100.0000.0

cache buffers lru91250.399.07,2141,9760.3

modify parameter v200.1 100.0000.0

redo copy00 92.399.31,4604000.0

什麼時候需要檢查latch統計呢?看下列項:

misses/gets的比率是多少

獲自spinning的misses的百分比是多少

latch請求了多少次

latch休眠了多少次

Redo copy latch看起來有很高的的失誤率,高達92.3%。不過,我們再仔細看的話,Redo copy

latches是獲自immediate模式。immediate模式的數值看起來還不錯,并且immediate模式隻有個别數大于willing to

wait模式。是以Redo copy latch其實并不存在競争。不過,看起來shared pool和library cache

latches可能存在競争。考慮執行一條查詢檢查latches的sleeps以确認是否确實存在問題。

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。

Latches調優

不要調整latches。如果你發現latch存在競争,它可能是部分SGA資源使用反常的征兆。要修正問題所在,你更多的是去檢查那部分SGA資源使用的競争情況。僅僅從v$latch是無法定位問題所在的。

關于latches的更多資訊可以浏覽Oracle Database Concepts。

資料庫中有些類别的latches擁有多個。V$LATCH中提供了每個類别的總計資訊。如果想看到單個latch,你可以通過查詢本視圖。

例如:

繼續閱讀