Oracle 多粒度鎖機制介紹
根據(jù)保護對象的不同,Oracle數(shù)據(jù)庫鎖可以分為以下幾大類:
(1) DML lock(data locks,數(shù)據(jù)鎖):用于保護數(shù)據(jù)的完整性;
(2) DDL lock(dictionary locks,字典鎖):用于保護數(shù)據(jù)庫對象的結(jié)構(gòu)(例如表、視圖、索引的結(jié)構(gòu)定義);
(3) Internal locks 和latches(內(nèi)部鎖與閂):保護內(nèi)部數(shù)據(jù)庫結(jié)構(gòu);
(4) Distributed locks(分布式鎖):用于OPS(并行服務(wù)器)中;
(5) PCM locks(并行高速緩存管理鎖):用于OPS(并行服務(wù)器)中。
在Oracle中最主要的鎖是DML(也可稱為data locks,數(shù)據(jù)鎖)鎖。從封鎖粒度(封鎖對象的大小)的角度看,Oracle DML鎖共有兩個層次,即行級鎖和表級鎖。
3.1 Oracle的TX鎖(行級鎖、事務(wù)鎖)
許多對Oracle不太了解的技術(shù)人員可能會以為每一個TX鎖代表一條被封鎖的數(shù)據(jù)行,其實不然。TX的本義是Transaction(事務(wù)),當 一個事務(wù)第一次執(zhí)行數(shù)據(jù)更改(Insert、Update、Delete)或使用SELECT… FOR UPDATE語句進行查詢時,它即獲得一個TX(事務(wù))鎖,直至該事務(wù)結(jié)束(執(zhí)行COMMIT或ROLLBACK操作)時,該鎖才被釋放。所以,一個TX 鎖,可以對應(yīng)多個被該事務(wù)鎖定的數(shù)據(jù)行(在我們用的時候多是啟動一個事務(wù),然后SELECT… FOR UPDATE NOWAIT)。
在Oracle的每行數(shù)據(jù)上,都有一個標志位來表示該行數(shù)據(jù)是否被鎖定。Oracle不像DB2那樣,建立一個鏈表來維護每一行被加鎖的數(shù)據(jù),這樣 就大大減小了行級鎖的維護開銷,也在很大程度上避免了類似DB2使用行級鎖時經(jīng)常發(fā)生的鎖數(shù)量不夠而進行鎖升級的情況。數(shù)據(jù)行上的鎖標志一旦被置位,就表 明該行數(shù)據(jù)被加X鎖,Oracle在數(shù)據(jù)行上沒有S鎖。
3.2 TM鎖(表級鎖)
3.2.1 意向鎖的引出
表是由行組成的,當我們向某個表加鎖時,一方面需要檢查該鎖的申請是否與原有的表級鎖相容;另一方面,還要檢查該鎖是否與表中的每一行上的鎖相容。 比如一個事務(wù)要在一個表上加S鎖,如果表中的一行已被另外的事務(wù)加了X鎖,那么該鎖的申請也應(yīng)被阻塞。如果表中的數(shù)據(jù)很多,逐行檢查鎖標志的開銷將很大, 系統(tǒng)的性能將會受到影響。為了解決這個問題,可以在表級引入新的鎖類型來表示其所屬行的加鎖情況,這就引出了"意向鎖"的概念。
意向鎖的含義是如果對一個結(jié)點加意向鎖,則說明該結(jié)點的下層結(jié)點正在被加鎖;對任一結(jié)點加鎖時,必須先對它的上層結(jié)點加意向鎖。如:對表中的任一行 加鎖時,必須先對它所在的表加意向鎖,然后再對該行加鎖。這樣一來,事務(wù)對表加鎖時,就不再需要檢查表中每行記錄的鎖標志位了,系統(tǒng)效率得以大大提高。
3.2.2 意向鎖的類型
由兩種基本的鎖類型(S鎖、X鎖),可以自然地派生出兩種意向鎖:
意向共享鎖(Intent Share Lock,簡稱IS鎖):如果要對一個數(shù)據(jù)庫對象加S鎖,首先要對其上級結(jié)點加IS鎖,表示它的后裔結(jié)點擬(意向)加S鎖;
意向排它鎖(Intent Exclusive Lock,簡稱IX鎖):如果要對一個數(shù)據(jù)庫對象加X鎖,首先要對其上級結(jié)點加IX鎖,表示它的后裔結(jié)點擬(意向)加X鎖。
另外,基本的鎖類型(S、X)與意向鎖類型(IS、IX)之間還可以組合出新的鎖類型,理論上可以組合出4種, 即:S+IS,S+IX,X+IS,X+IX,但稍加分析不難看出,實際上只有S+IX有新的意義,其它三種組合都沒有使鎖的強度得到提高 (即:S+IS=S,X+IS=X,X+IX=X,這里的"="指鎖的強度相同)。所謂鎖的強度是指對其它鎖的排斥程度。
這樣我們又可以引入一種新的鎖的類型:
共享意向排它鎖(Shared Intent Exclusive Lock,簡稱SIX鎖):如果對一個數(shù)據(jù)庫對象加SIX鎖,表示對它加S鎖,再加IX鎖,即SIX=S+IX。例如:事務(wù)對某個表加SIX鎖,則表示該 事務(wù)要讀整個表(所以要對該表加S鎖),同時會更新個別行(所以要對該表加IX鎖)。
這樣數(shù)據(jù)庫對象上所加的鎖類型就可能有5種:即S、X、IS、IX、SIX。
具有意向鎖的多粒度封鎖方法中任意事務(wù)T要對一個數(shù)據(jù)庫對象加鎖,必須先對它的上層結(jié)點加意向鎖。申請封鎖時應(yīng)按自上而下的次序進行;釋放封鎖時則 應(yīng)按自下而上的次序進行;具有意向鎖的多粒度封鎖方法提高了系統(tǒng)的并發(fā)度,減少了加鎖和解鎖的開銷。
3.3 Oracle的TM鎖(表級鎖)
Oracle的DML鎖(數(shù)據(jù)鎖)正是采用了上面提到的多粒度封鎖方法,其行級鎖雖然只有一種(即X鎖),但其TM鎖(表級鎖)類型共有5種,分別 稱為共享鎖(S鎖)、排它鎖(X鎖)、行級共享鎖(RS鎖)、行級排它鎖(RX鎖)、共享行級排它鎖(SRX鎖),與上面提到的S、X、IS、IX、 SIX相對應(yīng)。需要注意的是,由于Oracle在行級只提供X鎖,所以與RS鎖(通過SELECT … FOR UPDATE語句獲得)對應(yīng)的行級鎖也是X鎖(但是該行數(shù)據(jù)實際上還沒有被修改),這與理論上的IS鎖是有區(qū)別的。 鎖的兼容性是指當一個應(yīng)用程序在表(行)上加上某種鎖后,其他應(yīng)用程序是否能夠在表(行)上加上相應(yīng)的鎖,如果能夠加上,說明這兩種鎖是兼容的,否則說明 這兩種鎖不兼容,不能對同一數(shù)據(jù)對象并發(fā)存取。
下表為Oracle數(shù)據(jù)庫TM鎖的兼容矩陣(Y=Yes,表示兼容的請求; N=No,表示不兼容的請求;-表示沒有加鎖請求):
表五:Oracle數(shù)據(jù)庫TM鎖的相容矩陣

一方面,當Oracle執(zhí)行SELECT…FOR UPDATE、INSERT、UPDATE、DELETE等DML語句時,系統(tǒng)自動在所要操作的表上申請表級RS鎖(SELECT…FOR UPDATE)或RX鎖(INSERT、UPDATE、DELETE),當表級鎖獲得后,系統(tǒng)再自動申請TX鎖,并將實際鎖定的數(shù)據(jù)行的鎖標志位置位(指 向該TX鎖);另一方面,程序或操作人員也可以通過LOCK TABLE語句來指定獲得某種類型的TM鎖。下表是筆者總結(jié)了Oracle中各SQL語句產(chǎn)生TM鎖的情況:
表六:Oracle數(shù)據(jù)庫TM鎖小結(jié)

我們可以看到,通常的DML操作(SELECT…FOR UPDATE、INSERT、UPDATE、DELETE),在表級獲得的只是意向鎖(RS或RX),其真正的封鎖粒度還是在行級;另外,Oracle數(shù) 據(jù)庫的一個顯著特點是,在缺省情況下,單純地讀數(shù)據(jù)(SELECT)并不加鎖,Oracle通過回滾段(Rollback segment)來保證用戶不讀"臟"數(shù)據(jù)。這些都提高了系統(tǒng)的并發(fā)程度。
由于意向鎖及數(shù)據(jù)行上鎖標志位的引入,減小了Oracle維護行級鎖的開銷,這些技術(shù)的應(yīng)用使Oracle能夠高效地處理高度并發(fā)的事務(wù)請求。
Oracle 多粒度封鎖機制的監(jiān)控
為了監(jiān)控Oracle系統(tǒng)中鎖的狀況,我們需要對幾個系統(tǒng)視圖有所了解:
5.1 v$lock視圖
v$lock視圖列出當前系統(tǒng)持有的或正在申請的所有鎖的情況,其主要字段說明如下:
表七:v$lock視圖主要字段說明

其中在TYPE字段的取值中,本文只關(guān)心TM、TX兩種DML鎖類型;
5.2 v$locked_object視圖
v$locked_object視圖列出當前系統(tǒng)中哪些對象正被鎖定,其主要字段說明如下:
表八:v$locked_object視圖字段說明

5.3 Oracle鎖監(jiān)控腳本
根據(jù)上述系統(tǒng)視圖,可以編制腳本來監(jiān)控數(shù)據(jù)庫中鎖的狀況。
5.3.1 showlock.sql
第一個腳本showlock.sql,該腳本通過連接v$locked_object與all_objects兩視圖,顯示哪些對象被哪些會話鎖 ?。?/p>
|
<pre class="displaycode" name="code" style="box-sizing: border-box; outline: 0px; margin: 0px 0px 24px; padding: 8px; font-weight: normal; position: relative; white-space: pre-wrap; overflow-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 1em; line-height: 22px; color: rgb(0, 0, 0);">/* showlock.sql */
column o_name format a10
column lock_type format a20
column object_name format a15
select rpad(oracle_username,10) o_name,session_id sid,
decode(locked_mode,0,'None',1,'Null',2,'Row share',
3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,'Exclusive') lock_type,
object_name ,xidusn,xidslot,xidsqn
from vlocked_object.object_id=all_objects.object_id;
5.3.2 showalllock.sql
</pre>
|
第二個腳本showalllock.sql,該腳本主要顯示當前所有TM、TX鎖的信息;
|
<pre class="displaycode" name="code" style="box-sizing: border-box; outline: 0px; margin: 0px 0px 24px; padding: 8px; font-weight: normal; position: relative; white-space: pre-wrap; overflow-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 1em; line-height: 22px; color: rgb(0, 0, 0);">/* showalllock.sql */
select sid,type,id1,id2,
decode(lmode,0,'None',1,'Null',2,'Row share',
3,'Row Exclusive',4,'Share',5,'Share Row Exclusive',6,'Exclusive')
lock_type,request,ctime,block
from v$lock
where TYPE IN('TX','TM');</pre>
|