干貨 | Oracle數據庫有把TX鎖,如何定位鎖在哪?
數據庫是一個多用戶使用的共享資源,為了保證數據的一致性,加鎖是實現數據庫并發控制的一個非常重要的技術。當事務在對某個數據對象進行操作前,先向系統發出請求,對其加鎖,加鎖后事務就對該數據對象有了一定的控制,在該事務釋放鎖之前,其他的事務不能對此數據對象進行更新操作。
數據庫包含排它鎖(Exclusive Locks,即X鎖)和共享鎖(Share Locks,即S鎖)兩種基本的鎖類型,利用這兩種基本的鎖類型來對數據庫的事務進行并發控制。ORACLE數據庫根據保護的對象不同,將Oracle數據庫鎖分為以下幾大類:DML鎖(data locks,數據鎖),用于保證并發情況下的數據完整性;DDL鎖(dictionary locks,字典鎖),用于保護數據庫對象的結構,如表、索引等的結構定義;內部鎖和閂(internal locks and latches),保護數據庫的內部結構。
這里我們只討論DML鎖。在Oracle數據庫中,DML鎖主要包括TM鎖和TX鎖,其中TM鎖稱為表級鎖,TX鎖稱為事務鎖或行級鎖。當Oracle 執行DML語句時,系統自動在所要操作的表上申請TM類型的鎖。當TM鎖獲得后,系統再自動申請TX類型的鎖,并將實際鎖定的數據行的鎖標志位進行置位。
在數據行上只有X鎖(排他鎖)。在 Oracle數據庫中,當一個事務***發起一個DML語句時就獲得一個TX鎖,該鎖保持到事務被提交或回滾。當兩個或多個會話在表的同一條記錄上執行 DML語句時,***個會話在該條記錄上加鎖,其他的會話處于等待狀態。當***個會話提交后,TX鎖被釋放,其他會話才可以加鎖。
當Oracle數據庫發生TX鎖等待時,若處理不及時常會引起Oracle數據庫掛起或死鎖,產生ORA-00060的錯誤,導致應用出現長時間未響應、大量事務失敗等問題。
如何處理TX鎖等待
當數據庫中發生enq: TX - row lock contention行級鎖等待時,可以查詢v$session.blocking_session列或v$lock視圖來找到阻塞源,通過kill阻塞源來快速使業務恢復正常。
如何定位TX鎖具體行數據
在某些情況下,用戶想要了解經常發生TX鎖等待的具體是哪些數據。下面來展示如何獲取TX鎖的具體行數據。
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視圖聯合查詢來獲取具體行數據:
- 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的行發生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聯合查詢:
- 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';
可以發現得到的是帶有變量的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行數據中發生等待(數據是從0行開始的)
根據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
再通過函數ROWID_CREATE轉換得到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
可以發現,發生鎖等待的正是此行數據。
注:此種方法同樣適用于沒有使用綁定變量情況
如何避免TX鎖等待
避免使用select for update方式查詢數據
修改完數據后盡可能盡快提交
需要修改大量數據時,避免在業務高峰期間進行;如果可能,拆分成多個事務分批修改提交
附(dbms_rowid.ROWID_CREATE函數)
關于dbms_rowid.ROWID_CREATE函數,需要注意的是其中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