數(shù)據(jù)庫是一個多用戶使用的共享資源,為了保證數(shù)據(jù)的一致性,加鎖是實現(xiàn)數(shù)據(jù)庫并發(fā)控制的一個非常重要的技術(shù)。當(dāng)事務(wù)在對某個數(shù)據(jù)對象進行操作前,先向系統(tǒng)發(fā)出請求,對其加鎖,加鎖后事務(wù)就對該數(shù)據(jù)對象有了一定的控制,在該事務(wù)釋放鎖之前,其他的事務(wù)不能對此數(shù)據(jù)對象進行更新操作。
數(shù)據(jù)庫包含排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)兩種基本的鎖類型,利用這兩種基本的鎖類型來對數(shù)據(jù)庫的事務(wù)進行并發(fā)控制。ORACLE數(shù)據(jù)庫根據(jù)保護的對象不同,將Oracle數(shù)據(jù)庫鎖分為以下幾大類:DML鎖(data locks,數(shù)據(jù)鎖),用于保證并發(fā)情況下的數(shù)據(jù)完整性;DDL鎖(dictionary locks,字典鎖),用于保護數(shù)據(jù)庫對象的結(jié)構(gòu),如表、索引等的結(jié)構(gòu)定義;內(nèi)部鎖和閂(internal locks and latches),保護數(shù)據(jù)庫的內(nèi)部結(jié)構(gòu)。
這里我們只討論DML鎖。在Oracle數(shù)據(jù)庫中,DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱為表級鎖,TX鎖稱為事務(wù)鎖或行級鎖。當(dāng)Oracle 執(zhí)行DML語句時,系統(tǒng)自動在所要操作的表上申請TM類型的鎖。當(dāng)TM鎖獲得后,系統(tǒng)再自動申請TX類型的鎖,并將實際鎖定的數(shù)據(jù)行的鎖標(biāo)志位進行置位。
在數(shù)據(jù)行上只有X鎖(排他鎖)。在 Oracle數(shù)據(jù)庫中,當(dāng)一個事務(wù)首次發(fā)起一個DML語句時就獲得一個TX鎖,該鎖保持到事務(wù)被提交或回滾。當(dāng)兩個或多個會話在表的同一條記錄上執(zhí)行 DML語句時,第一個會話在該條記錄上加鎖,其他的會話處于等待狀態(tài)。當(dāng)?shù)谝粋€會話提交后,TX鎖被釋放,其他會話才可以加鎖。
當(dāng)Oracle數(shù)據(jù)庫發(fā)生TX鎖等待時,若處理不及時常會引起Oracle數(shù)據(jù)庫掛起或死鎖,產(chǎn)生ORA-00060的錯誤,導(dǎo)致應(yīng)用出現(xiàn)長時間未響應(yīng)、大量事務(wù)失敗等問題。
如何處理TX鎖等待
當(dāng)數(shù)據(jù)庫中發(fā)生enq: TX - row lock contention行級鎖等待時,可以查詢v$session.blocking_session列或v$lock視圖來找到阻塞源,通過kill阻塞源來快速使業(yè)務(wù)恢復(fù)正常。
如何定位TX鎖具體行數(shù)據(jù)
在某些情況下,用戶想要了解經(jīng)常發(fā)生TX鎖等待的具體是哪些數(shù)據(jù)。下面來展示如何獲取TX鎖的具體行數(shù)據(jù)。
1、SQL語句中沒有使用綁定變量
Session 1:
SQL> update t1 set b=10 where a=3;
Session 2:
SQL> update t1 set b=99 where a=3;
此時,可以通過v$sql和v$session視圖聯(lián)合查詢來獲取具體行數(shù)據(jù):
select sql_text from v$sql a,v$session b where a.sql_id=b.sql_id and b.event='enq: TX - row lock contention';
可以得知t1表中a=3的行發(fā)生TX鎖等待
2、SQL語句中使用綁定變量
Session 1:
SQL> variable v_a number;
SQL> exec :v_a :=3;
SQL> update t1 set b=10 where a=:v_a;
Session 2:
SQL> variable v_a number;
SQL> exec :v_a :=3;
SQL> update t1 set b=99 where a=:v_a;
通過v$sql和v$session聯(lián)合查詢:
select sql_text from v$sql a,v$session b where a.sql_id=b.sql_id and b.event='enq: TX - row lock contention';
可以發(fā)現(xiàn)得到的是帶有變量的SQL,無法定位具體的行。
此時可以通過以下SQL得到具體鎖行信息:
SELECT row_wait_obj#, row_wait_file#, row_wait_block#, row_wait_row#
FROM v$session
WHERE event='enq: TX - row lock contention';
這4個列所代表的意思如下:
object_id為14255的對象,在4號文件的133塊中的第1行數(shù)據(jù)中發(fā)生等待(數(shù)據(jù)是從0行開始的)
根據(jù)object_id得到表名和data_object_id
SQL> select owner||'.'||object_name tab_name ,data_object_id from dba_objects where object_id=14255;
TEST.T1 14296
再通過函數(shù)ROWID_CREATE轉(zhuǎn)換得到ROWID
SQL> select dbms_rowid.ROWID_CREATE(1, 14296,4,133,1) from dual; ---14296指的是dba_objects.data_object_id
AAADfYAAEAAAACFAAB
SQL> select * from TEST.T1 where rowid='AAADfYAAEAAAACFAAB';
A B
3 4
可以發(fā)現(xiàn),發(fā)生鎖等待的正是此行數(shù)據(jù)。
注:此種方法同樣適用于沒有使用綁定變量情況
如何避免TX鎖等待
避免使用select for update方式查詢數(shù)據(jù)
修改完數(shù)據(jù)后盡可能盡快提交
需要修改大量數(shù)據(jù)時,避免在業(yè)務(wù)高峰期間進行;如果可能,拆分成多個事務(wù)分批修改提交
附(dbms_rowid.ROWID_CREATE函數(shù))
關(guān)于dbms_rowid.ROWID_CREATE函數(shù),需要注意的是其中OBJECT_NUMBER的輸入值是dba_objects.data_object_id。
FUNCTION ROWID_CREATE RETURNS ROWID
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
ROWID_TYPE NUMBER IN
OBJECT_NUMBER NUMBER IN
RELATIVE_FNO NUMBER IN
BLOCK_NUMBER NUMBER IN
ROW_NUMBER NUMBER IN