1. 程式人生 > >enq: TX - row lock contention故障處理一則

enq: TX - row lock contention故障處理一則

tid ces mod pen commit 驗證 mode 並且 rac

一個非常easy的問題,之所以讓我對這個問題進行總結。一是由於沒我想象的簡單,在處理的過程中遇到了一些磕磕碰碰,甚至繞了一些彎路。二是引發了我對故障處理時的一些思考。
6月19日,下午5點左右。數據庫出現了大量的enq: TX - row lock contention等待事件,依照以往的經驗,這類等待一般與業務邏輯有關。DBA可以做的事情。一般就是將鎖等待著的連接信息,等待鎖的SQL語句。甚至等待的詳細數據行,還有就是鎖持有者的連接信息,造成鎖等待的SQL語句等一些基本信息提交給開發者,改動業務邏輯。


註意

  • 非常多情況下,改動業務邏輯不是一時半會能夠馬上解決的事,有時候假設鎖持有或者鎖等待業務不是特別重要。能夠與應用維護人員協商,先停掉這部分業務,讓其他業務正常執行
  • 非常多時候。數據庫僅僅有一個鎖持有者。並且這個鎖持有的連接也沒有活動,能夠與應用維護人員協商,嘗試kill鎖持有者,看看興許鎖等待是否能自己主動解決。

    這樣的情況通常是一個單獨的連接出現僵死導致。kill掉持有者以後,問題自然就攻克了

  • 第二種情況就是,前天晚上上線後,平時執行好好的業務,也沒出現鎖等待。有一天出現了這類問題。並且kill掉鎖持有者以後。問題無法得到解決(業務邏輯問題,kill掉以後,興許還會發生)。這通常是前天晚上上線引起。

本來認為數據庫層提供信息非常easy。結果與想象的有點差別,來看一下詳細的過程 (1)查詢鎖信息。例如以下
SESS
LMODE LMODE REQUEST TYPE EVENT SQL_TEXT
Holder: 4266 exclusive 6 0 TX SQL*Net message from client
Waiter: 3136 none 0 4 TX enq: TX - row lock contention insert into xxxxx(ID,xxx,xxxx,xxx,….) values(seq_xxx.nextval,:"SYS_B_0",:"SYS_B_1",:"SYS_B_2",:"SYS_B_3",:"SYS_B_4",:"SYS_B_5")
Holder: 2276 exclusive 6 0 TX SQL*Net message from client
Waiter: 1716 none 0 4 TX enq: TX - row lock contention insert into xxxxx(ID,xxx,xxxx,xxx,….) values(seq_xxx.nextval,:"SYS_B_0",:"SYS_B_1",:"SYS_B_2",:"SYS_B_3",:"SYS_B_4",:"SYS_B_5")
Holder: 1288 exclusive 6 0 TX SQL*Net message from client
Waiter: 1565 none 0 4 TX enq: TX - row lock contention insert into xxxxx(ID,xxx,xxxx,xxx,….) values(seq_xxx.nextval,:"SYS_B_0",:"SYS_B_1",:"SYS_B_2",:"SYS_B_3",:"SYS_B_4",:"SYS_B_5")
Holder: 1000 exclusive 6 0 TX SQL*Net message from client
Waiter: 1147 none 0 4 TX enq: TX - row lock contention insert into xxxxx(ID,xxx,xxxx,xxx,….) values(seq_xxx.nextval,:"SYS_B_0",:"SYS_B_1",:"SYS_B_2",:"SYS_B_3",:"SYS_B_4",:"SYS_B_5")
Holder: 2989 exclusive 6 0 TX SQL*Net message from client
Waiter: 862 none 0 4 TX enq: TX - row lock contention insert into xxxxx(ID,xxx,xxxx,xxx,….) values(seq_xxx.nextval,:"SYS_B_0",:"SYS_B_1",:"SYS_B_2",:"SYS_B_3",:"SYS_B_4",:"SYS_B_5")
備註:表名和列名做了模糊化 能夠看到,鎖等待語句正在等待Insert條記錄 (2)通過查看鎖持有者,已經運行的語句,來推斷究竟是那個語句造成了鎖等待,查詢語句例如以下: select b.sql_text ,a.* from v$open_cursor a,v$sql b where a.sql_id=b.sql_id and a.sid=4266 and upper(b.sql_text) like ‘%xxxxx%‘; (3) 依據經驗insert一條語句被堵塞,通常是因為主鍵約束引起(還有一個連接也插入了同一條語句或者刪除了一條語句,可是沒有提交) 可是我通過上面的語句查詢的時候。發現怎麽也找不到鎖持有者有運行過這個表的不論什麽DML,並且詢問開發者,他們也說沒有對這張表的DML操作 當中open_cursor為1000,v$open_cursor中的記錄也遠遠沒有達到這個數,才100條不到。

session_cached_cursors設置為200。沒有道理這個連接運行的語句遊標已經被刷新出去 (4)還真沒有遇到過類似的問題。怎麽也找不到。這時我換了一個想法。拋開那些經驗。我在想,是不是有一種可能不正確Insert插入語句進行不論什麽DML操作,也會造成一條插入語句被鎖掉?? 我考慮了這張表的依賴對象是不是會造成種類等待,比如觸發器、外鍵引用等等。

細致考慮一番,發現觸發器,審計什麽的,數據庫應該能定位到詳細的語句,而不是發生在這個insert語句本身(就算是遞歸語句。Oracle也能捕獲到才對),因此,最讓我懷疑的就是外鍵引用。通過以下這個查詢。推斷是否這個表通過外鍵引用了其它對象,例如以下 select a.table_name, a.owner, a.constraint_name, a.constraint_type, a.r_owner, a.r_constraint_name,--被外鍵引用的約束名 b.table_name --被外鍵引用的表名 from dba_constraints a, dba_constraints b where a.constraint_type = ‘R‘ and a.r_constraint_name = b.constraint_name and a.r_owner = b.owner and b.table_name = ‘xxxxx‘ and b.owner=‘‘; 查詢發現,確實有一張表引用這個插入等待的表,這時,頓時感覺希望非常大。

(5)通過一個簡單的測試,我驗證我的猜測。例如以下 create table t3 (id number primary key,name varchar2(20),product_id number); create table t2 (id number primary key,name varchar2(20)); alter table t3 add constraint FK_PRODUCTSTAT_PRODUCTID foreign key (PRODUCT_id) references t2 (ID); SQL> insert into t2 values(1,‘dh‘); 1 row inserted SQL> insert into t2 values(2,‘cc‘); 1 row inserted SQL> insert into t2 values(3,‘cc‘); 1 row inserted SQL> commit; Commit complete session 1運行例如以下操作: SQL> select * from t2; ID NAME ---------- -------------------- 1 dh 2 cc 3 cc SQL> select * from t3; ID NAME PRODUCT_ID ---------- -------------------- ---------- --能夠看到,這時t3表有不論什麽記錄 SQL> insert into t2 values(4,‘cc‘); --對父表運行一條插入 1 row inserted session2 t2表運行一條插入操作,例如以下 insert into t3 values(1,‘tt‘,4); 令人驚喜的是,確實發生了鎖等待。與我們遇到的鎖等待類型一模一樣。 (6)查詢鎖持有者。是否有對鎖等待表的父表有進行DML操作。例如以下 select b.sql_text ,a.* from v$open_cursor a,v$sql b where a.sql_id=b.sql_id and a.sid=4266 and upper(b.sql_text) like ‘%xxxxx_ref%‘; 檢查結果與我們預期的一致,確實有非常多對主表的插入操作!

(7)基本我們已經確定是什麽語句導致鎖阻塞,將語句提交給開發者。改動代碼後,問題解決!


問題總結 事實上這個問題本身不難。值得思考的是,為什麽一個這麽簡單的問題,無法馬上找到原因。說究竟。非常多時候都是經驗束縛了我們,在遇到這類問題時。我們須要拋開已有的那些經驗。通過數據庫的原理來發現根本原因。因此,理論知識再怎麽強調都只是分,它真的非常重要。理解了原理,你才幹夠舉一反三。遊刃有余,而不是每次一碰到沒見過的問題都戰戰兢兢!

enq: TX - row lock contention故障處理一則