Oracle數據表死鎖的解決方法

一個簡單的排查和解決方法

死鎖時會報錯:ORA-00060: deadlock detected while waiting for resourcelinux

對應的中文報錯是:ORA-00060: 等待資源時檢測到死鎖數據庫

執行下面的SQL,查看被鎖的表:session

select object_name, machine, s.sid, s.serial#
  from v$locked_object l, dba_objects o, v$session s
 where l.object_id = o.object_id
   and l.session_id = s.sid

執行下面的SQL能夠強制解鎖架構

alter system kill session '277,1817'
--其中277對應上句SQL查出來的sid字段, 1817對應serial字段具體的值

具體的操做能夠看下面的實例,更加直觀的理解死鎖,而且去解決死鎖oracle

經過一個實例操做來更深刻的理解

參考自一個常見的ORA-00060死鎖現象app

在Oracle數據庫中若是出現死鎖現象,數據庫就會報ORA-00060的錯誤代碼,這種死鎖現象一般都是應用邏輯設計出錯致使的異常,和數據庫自己的設計無關,如今經過實驗模擬一個死鎖現象:學習

模擬死鎖現象

必定要本身實際動手去操做一下,也就花1個小時的時間,不過能夠對死鎖有一個很直觀的認知!測試

建立一個用於測試的數據表,而且添加幾條測試數據:設計

create table practice(uno varchar(8), uname varchar(20));

insert into practice values ('198', 'xm198-1');
insert into practice values ('198', 'xm198-2');
insert into practice values ('200', 'xm200-1');
insert into practice values ('200', 'xm200-2');
commit;

打開一個PLSQL,在PLSQL中打開兩個Command Window執行下列更新順序(下面的會話就是指Command Window)日誌

會話1:執行對uno爲198的字段更新,注意不執行commit;

SQL> update practice set uname = 'cj' where uno = '198';
2 rows updated

會話2:執行對uno爲200的字段更新,注意不執行commit;

SQL> update practice set uname = 'hh' where uno = '200';
2 rows updated

會話1:再執行對uno爲200的字段更新,注意不執行commit;,此時語句已經hang住(也就是卡住了,不像上面兩次的執行會輸出2 rows updated這樣的結果信息),須要等到會話2發出commit或者rollback動做

SQL> update practice set uname = 'cj' where uno = '200';   ---會話1在這裏hang住了

會話2:一旦執行下面的更新,會話2也會hang住,回到會話1就會發現會話1報錯

SQL> update practice set uname = 'sdf' where uno = '198';

回到會話1,能夠看到會話1報錯信息

SQL> update practice set uname = 'cj' where uno = '200';
update practice set uname = 'cj' where uno = '200'
ORA-00060: 等待資源時檢測到死鎖

查詢alert日誌發現報錯:ORA-00060: Deadlock detected. More info in file /u01/app/oracle/admin/prod/udump/prod_ora_4273.trc.

詳細解釋一下這種狀況的死鎖,隨便問一我的死鎖是怎麼產生的,懂點計算機知識的人就會說是循環等待,那麼這個例子中怎麼出現循環等待的呢:

  • 會話1先去更新uno = '198'的記錄,可是沒有commit或者rollback,那麼會話1就一直「佔用」 uno = '198'的記錄

  • 一樣會話2先去更新 uno = '200'的記錄,可是也沒有commit或者rollback,那麼會話2就一直「佔用」 uno= '200' 的記錄

  • 接着會話1又去嘗試更新uno = '200' 的記錄,可是這些記錄已經被會話2佔用了,因此就出現了會話1 hang住的狀況,其實就是會話1在等待會話2「釋放」它所「佔用」的 uno = '200'的記錄

  • 接着會話2又去嘗試更新 uno = '198' 的記錄,這樣就和會話1產生了循環等待

  • 因此也就產生了死鎖

  • 數據庫使用SQL修改數據時是要加鎖的,通常是行級鎖,因此上面所謂的佔用其實就是對符合條件的行加鎖,由於沒有執行commit或者rollback,因此就一直加鎖

  • 這一點須要再去深刻學習數據庫鎖SQL的執行原理數據庫原理等知識,以更加深刻的學習數據庫知識,而不要只是停留在目前淺顯的層面

  • 另外就像上面所說的,若是本身開發的程序在運行過程當中出現死鎖的問題:

    • 這種死鎖現象一般都是應用邏輯設計出錯致使的異常,和數據庫自己的設計無關

    • 因此須要檢查本身的程序在涉及到數據庫操做的方面是否是設計的有問題

    • 若是是一個簡單的程序可能查起來很簡單

    • 可是若是是一個大的項目,有不少的開發組均可能涉及到對數據庫的操做,那麼查起來可能就會比較難了

    • 這就須要本身有更大的更宏觀的角度,可以對整個大的項目架構有一個把握,另外就是可以對數據庫原理層面的知識有深刻的理解和掌握

補充說明:

若是在會話2中第二次不執行上面的更新SQL,而是執行commit;或者rollback;那麼會話1就不會報錯

好比會話2執行

SQL> rollback;
Rollback complete

回去檢查會話1,發現以前hang住的地方,如今已經解決了

SQL> update practice set uname = 'cj' where uno = '200';    --以前是hang在這裏沒有辦法更新的,如今由於會話2執行rollback,因此會話1從hang住恢復過來了
2 rows updated

強制解鎖

注意這裏須要使用系統用戶,由於通常用戶是沒有權限的,我是從新打開一個PLSQL,使用SYSTEM用戶登錄進行下面的操做的,再在新的PLSQL中打開一個Command Window。之因此從新打開一個PLSQL,是爲了保證可以不影響原有的PLSQL中登陸用戶的狀況下,使用SYSTEM用戶進行登陸。固然新不新開PLSQL只是表面現象而已,沒有必要深究什麼!

在新的PLSQL的會話中,經過dba_blockers表中的HOLDING_SESSION字段能夠查詢到hang住會話的ID:

SQL> select * from dba_blockers;
HOLDING_SESSION
---------------
             76

使用v$session視圖獲取hang住會話的sid和serial#

SQL> select sid,serial#,username from v$session where sid in (select blocking_session from v$session);
       SID    SERIAL# USERNAME
---------- ---------- ------------------------------
        76        271 TRADE

找到hang住的會話後,執行alter system命令kill掉相應的session就能夠了:

SQL> alter system kill session '76,271' immediate;
System altered

檢查以前的會話來確認一下

執行後上面的強制解鎖以後,返回前一個PLSQL,檢查其中的兩個會話,會話1中的會話會自動被kill掉

在會話1執行查詢SQL,會發現報錯:

SQL> select * from practice;
Warning: connection was lost and re-established
UNO      UNAME
-------- --------------------
198      xm198-1
198      xm198-2
200      xm200-1
200      xm200-2

會話2中執行查詢發現會話2的更改生效。在會話2中執行下面查詢SQL,沒有報錯,其實當前

SQL> select * from practice;
UNO      UNAME
-------- --------------------
198      sdf
198      sdf
200      hh
200      hh

可是再在當前的PLSQL打開一個Command Window(叫會話3),執行查詢語句,發現結果和會話2不一致

SQL> select * from practice;
UNO      UNAME
-------- --------------------
198      xm198-1
198      xm198-2
200      xm200-1
200      xm200-2

回到會話2,發現尚未提交,因此在提交後,再執行查詢看結果

SQL> commit;
Commit complete

SQL> select * from practice;
UNO      UNAME
-------- --------------------
198      sdf
198      sdf
200      hh
200      hh

而後再回到會話3,執行查詢語句,發現如今和會話2同樣了

SQL> select * from practice;
UNO      UNAME
-------- --------------------
198      sdf
198      sdf
200      hh
200      hh

實際上,當出現死鎖的狀況,Oracle也會在一段時間後解鎖。這種狀況會在alert日誌中記載下列信息:ORA-00060: Deadlock detected. More info in file /u01/app/oracle/admin/ORCL/udump/orcl_ora_3173.trc.linux

相關文章
相關標籤/搜索