干貨 | Oracle數(shù)據(jù)庫有把TX鎖,如何定位鎖在哪?
數(shù)據(jù)庫是一個(gè)多用戶使用的共享資源,為了保證數(shù)據(jù)的一致性,加鎖是實(shí)現(xiàn)數(shù)據(jù)庫并發(fā)控制的一個(gè)非常重要的技術(shù)。當(dāng)事務(wù)在對(duì)某個(gè)數(shù)據(jù)對(duì)象進(jìn)行操作前,先向系統(tǒng)發(fā)出請(qǐng)求,對(duì)其加鎖,加鎖后事務(wù)就對(duì)該數(shù)據(jù)對(duì)象有了一定的控制,在該事務(wù)釋放鎖之前,其他的事務(wù)不能對(duì)此數(shù)據(jù)對(duì)象進(jìn)行更新操作。
數(shù)據(jù)庫包含排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)兩種基本的鎖類型,利用這兩種基本的鎖類型來對(duì)數(shù)據(jù)庫的事務(wù)進(jìn)行并發(fā)控制。ORACLE數(shù)據(jù)庫根據(jù)保護(hù)的對(duì)象不同,將Oracle數(shù)據(jù)庫鎖分為以下幾大類:DML鎖(data locks,數(shù)據(jù)鎖),用于保證并發(fā)情況下的數(shù)據(jù)完整性;DDL鎖(dictionary locks,字典鎖),用于保護(hù)數(shù)據(jù)庫對(duì)象的結(jié)構(gòu),如表、索引等的結(jié)構(gòu)定義;內(nèi)部鎖和閂(internal locks and latches),保護(hù)數(shù)據(jù)庫的內(nèi)部結(jié)構(gòu)。
這里我們只討論DML鎖。在Oracle數(shù)據(jù)庫中,DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱為表級(jí)鎖,TX鎖稱為事務(wù)鎖或行級(jí)鎖。當(dāng)Oracle 執(zhí)行DML語句時(shí),系統(tǒng)自動(dòng)在所要操作的表上申請(qǐng)TM類型的鎖。當(dāng)TM鎖獲得后,系統(tǒng)再自動(dòng)申請(qǐng)TX類型的鎖,并將實(shí)際鎖定的數(shù)據(jù)行的鎖標(biāo)志位進(jìn)行置位。
在數(shù)據(jù)行上只有X鎖(排他鎖)。在 Oracle數(shù)據(jù)庫中,當(dāng)一個(gè)事務(wù)***發(fā)起一個(gè)DML語句時(shí)就獲得一個(gè)TX鎖,該鎖保持到事務(wù)被提交或回滾。當(dāng)兩個(gè)或多個(gè)會(huì)話在表的同一條記錄上執(zhí)行 DML語句時(shí),***個(gè)會(huì)話在該條記錄上加鎖,其他的會(huì)話處于等待狀態(tài)。當(dāng)***個(gè)會(huì)話提交后,TX鎖被釋放,其他會(huì)話才可以加鎖。
當(dāng)Oracle數(shù)據(jù)庫發(fā)生TX鎖等待時(shí),若處理不及時(shí)常會(huì)引起Oracle數(shù)據(jù)庫掛起或死鎖,產(chǎn)生ORA-00060的錯(cuò)誤,導(dǎo)致應(yīng)用出現(xiàn)長(zhǎng)時(shí)間未響應(yīng)、大量事務(wù)失敗等問題。
如何處理TX鎖等待
當(dāng)數(shù)據(jù)庫中發(fā)生enq: TX - row lock contention行級(jí)鎖等待時(shí),可以查詢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;
此時(shí),可以通過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,無法定位具體的行。
此時(shí)可以通過以下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個(gè)列所代表的意思如下:
object_id為14255的對(duì)象,在4號(hào)文件的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ù)時(shí),避免在業(yè)務(wù)高峰期間進(jìn)行;如果可能,拆分成多個(gè)事務(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