解決Oracle數據庫死鎖

解決Oracle數據庫死鎖
  介紹
  本文我們嘗試總結在多個用戶併發情況下,如何識別和解決刪除操作期間發生的死鎖問題,在開始之前,我們先簡單描述一下什麼是死鎖以及什麼東西會導致死鎖。
  死鎖
  在任何數據庫中發生死鎖都是不愉快的,即使是在一個特殊的情況下發生也是如此,它們會減小應用程序的接受程度(ACCEPTANCE),因此避免並正確解釋死鎖是非常重要的。
  當兩個或更多用戶相互等待鎖定的數據時就會發生死鎖,發生死鎖時,這些用戶被卡住不能繼續處理業務,Oracle自動檢測死鎖並解決它們(通過回滾一個包含在死鎖中的語句實現),釋放掉該語句鎖住的數據,回滾的會話將會遇到Oracle錯誤“ORA-00060:等待資源時檢測到死鎖”。
  是什麼導致了死鎖?
  明確地鎖定表是爲了保證讀/寫一致性,未建立索引的外鍵約束,在相同順序下表不會鎖住,沒有爲數據段分配足夠的存儲參數(主要是指INITTRANS,MAXTRANS和PCTFREE參數)很容易引發突發鎖和死鎖,原因是多種多樣的,需要重新逐步審查。
  識別死鎖
  當Oracle數據庫檢測到死鎖時(Oracle錯誤消息:ORA-00060),相應的消息就寫入到警告日誌文件中(alert.log),另外還會在USER_DUMP_DEST目錄下創建一個跟蹤文件,分析警告日誌文件和跟蹤文件是非常耗時的。
  下面是一個警告日誌文件示例:
  Mon Aug 07 09:14:42 2007
  ORA-000060: Deadlock detected. More info in file
  e:\oracle\admin\GEDEON\udump\ORA01784.TRC.
  下面是從跟蹤文件中節選出來的片段,從其中我們可以看出是哪個語句創造了死鎖,相關的語句和被鎖定的資源已經標記爲粗體。
  /users/ora00/log/odn_ora_1097872.trc
  Oracle9i Enterprise Edition Release 9.2.0.8.0 - 64bit Production
  With the Partitioning, OLAP and Oracle Data Mining options
  JServer Release 9.2.0.8.0 - Production
  ORACLE_HOME = /soft/ora920
  System name:AIX
  Node name:beaid8
  Release:2
  Version:5
  Machine:00C95B0E4C00
  Instance name: ODN
  Redo thread mounted by this instance: 1
  Oracle process number: 17
  Unix process pid: 1097872, image: oracle@beaid8 (TNS V1-V3)
  *** 2007-06-04 14:41:04.080
  *** SESSION ID:(10.6351) 2007-06-04 14:41:04.079
  DEADLOCK DETECTED ( ORA-00060 )
  The following deadlock is not an ORACLE error. It is a
  deadlock due to user error in the design of an application
  or from issuing incorrect ad-hoc SQL. The following
  information may aid in determining the deadlock:
  Deadlock graph:
  ---------Blocker(s)-------- ---------Waiter(s)---------
  Resource Name process session holds waits process session holds waits
  TM-00001720-00000000 17 10 SX 16 18 SX SSX
  TM-0000173a-00000000 16 18 SX 17 10 SX SSX
  session 10: DID 0001-0011-00000002session 18: DID 0001-0010-00000022
  session 18: DID 0001-0010-00000022session 10: DID 0001-0011-00000002
  Rows waited on:
  Session 18: obj - rowid = 00001727 - AAABcnAAJAAAAAAAAA
  (dictionary objn - 5927, file - 9, block - 0, slot - 0)
  Session 10: obj - rowid = 00001727 - AAABcnAAJAAAAAAAAA
  (dictionary objn - 5927, file - 9, block - 0, slot - 0)
  Information on the OTHER waiting sessions:
  Session 18:
  pid=16 serial=2370 audsid=18387 user: 21/ODN
  O/S info: user: mwpodn00, term: unknown, ospid: , machine: beaida
  program: JDBC Thin Client
  application name: JDBC Thin Client, hash value=0
  Current SQL Statement:
  DELETE FROM ODNQTEX WHERE EX_ID = :B1
  End of information on OTHER waiting sessions.
  Current SQL statement for this session:
  DELETE FROM ODNQTFN WHERE FN_ID_EXIGENCE_EX = :B1
  ----- PL/SQL Call Stack -----
  object line object
  handle number name
  7000000135f7fd8 34 procedure ODN.ODNQPDR
  7000000135f89f0 16 procedure ODN.ODNQPZB
  我們可以使用企業管理器來決定保留所有的鎖還是釋放掉它們,爲了便於說明,我們打開2個sqlplus實例會話(在此期間同時發生了死鎖)來一起調式,當每個語句執行完畢後,我們看到鎖仍然保留下來了,它可以幫助我們識別出是哪個資源引起的死鎖。
  下面列出了可以幫助我們監視鎖的視圖:

  可以通過查詢V$LOCK字典視圖來確定鎖,如:

select * from v$lock ;
  下面的SQL查詢可以用於確定鎖住數據庫對象的鎖:
  select
  c.owner,
  c.object_name,
  c.object_type,
  b.sid,
  b.serial#,
  b.status,
  b.osuser,
  b.machine
  from
  v$locked_object a ,
  v$session b,
  dba_objects c
  where
  b.sid = a.session_id
  and
  a.object_id = c.object_id;
  解決死鎖
  安裝順序執行下面的修改,避免一致性訪問期間的死鎖問題:
  設置事務一致性:我們需要確定一個隔離水平,在存儲過程中可以使用“READ COMMITTED”或“SERIALIZABLE”,我們需要考慮兩件事情:
   設置事務可以在任何時間提交
   相關表中行的讀取順序
  Oracle數據庫隔離模式通過行級鎖和Oracle數據庫多版本併發控制系統提供高級一致性和併發性(和高性能),READ COMMITTED模式可以提供更多的併發性,因爲沒有重複讀,SERIALIZABLE隔離水平提供了更好的一致性,通過保護非重複讀實現,在一個讀寫事務執行不止一次查詢時這很重要,然而,SERIALIZABLE模式需要應用程序檢查“不能連續訪問”的錯誤,這樣就可以在有許多訪問相同數據的一致性事務的環境中大大減少吞吐量。
  在我們的例子中,我們需要使用READ COMMITTED隔離水平,原因如下:
  我們需要獲取由另一個事務提交的查詢返回的行,不僅僅是獲取剛開始的事務返回的行。
  如果我們將隔離水平設爲SERIALIZABLE,將會獲得一個“事務無法按順序訪問”的錯誤,因爲我們想要修改的數據已經被另一個事務修改了。
  在存儲過程中我們放入下面的語句:
  SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
  明確鎖定數據:Oracle數據庫總是執行必要的鎖確保數據的併發性、完整性和語句級讀一致性,但在我們的例子中,我們需要獨佔訪問資源,爲了處理它的語句,事務獨佔訪問資源,不要等待其他事務完成。
  我將會用一個例子來解釋,在我們的案例中,需要刪除一個業務對象System,爲了刪除這個對象,首先我們需要刪除System子表中的所有數據,現在進入其中一個子表,我們在子表上創建一個關於刪除的觸發器,這個觸發器更新主表System的數據並鎖住它,接下來進入刪除操作,當我們想刪除System表中的數據時,就會出現死鎖,因爲它已經被前面子表上的觸發器給鎖住了,爲了避免出現這種情況,在刪除操作開始之前,我們提供一個行獨佔鎖鎖住System表來解決這個問題。命令如下:
  LOCK TABLE ODNQTSY IN ROW EXCLUSIVE MODE;
  並行索引和查詢處理:Oracle數據庫使用索引增強SQL查詢的性能,這有助於我們執行DML操作,如插入、更新和刪除,做這些動作的時間將會極具減少了。
  並行索引將會讓優化器思考執行並行查詢時使用索引,這是避免死鎖的一個方法,當索引重建後,Oracle將會允許多個DML操作發生在同一個索引上,在索引上啓用並行操作的語法如下:
  ALTER INDEX PARALLEL;
  使用並行查詢選項的最大好處是直接路徑讀取,因此需要的latch就更少了,同樣,並行執行大大減少了數據密集型業務的響應時間。
  並行執行選項可以通過修改INIT.ORA文件中的PARALLEL_AUTOMATIC_TUNING參數(設爲TRUE)實現數據庫級別的啓用。
  parallel_automatic_tuning=TRUE
  外鍵上的索引:外鍵上如果沒有建立索引會引發兩個問題,第一個是如果你更新父記錄主鍵或刪除父記錄,子表的外鍵沒有索引時,會引發表級鎖;第二個問題是性能。
  如果你試圖刪除父錶行,或更新父/子關聯中父錶行的鍵值,而子表的外鍵上沒有索引時,Oracle將會嘗試在子表上獲得一個共享行級獨佔鎖,接下來如果有其它會話要修改子表,它將不得不等待在它前面的SRX鎖(共享行級獨佔鎖),這樣就形成了一個死鎖狀態。
  下面的腳本可以幫助我們識別沒有索引的外鍵(FK)約束,從腳本執行的輸出中,我們可以確定在外鍵上創建索引將可以幫助我們改善性能,並且可以避免死鎖。
  column columns format a20 word_wrapped
  column table_name format a30 word_wrapped
  select decode( b.table_name, NULL, '****', 'ok' ) Status,
  a.table_name, a.columns, b.columns
  from
  ( select substr(a.table_name,1,30) table_name,
  substr(a.constraint_name,1,30) constraint_name,
  max(decode(position, 1, substr(column_name,1,30),NULL)) ||
  max(decode(position, 2,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position, 3,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position, 4,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position, 5,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position, 6,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position, 7,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position, 8,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position, 9,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position,10,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position,11,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position,12,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position,13,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position,14,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position,15,', '||substr(column_name,1,30),NULL)) ||
  max(decode(position,16,', '||substr(column_name,1,30),NULL)) columns
  from user_cons_columns a, user_constraints b
  where a.constraint_name = b.constraint_name
  and b.constraint_type = 'R'
  group by substr(a.table_name,1,30), substr(a.constraint_name,1,30) ) a,
  ( select substr(table_name,1,30) table_name, substr(index_name,1,30) index_name,
  max(decode(column_position, 1, substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 2,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 3,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 4,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 5,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 6,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 7,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 8,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position, 9,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position,10,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position,11,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position,12,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position,13,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position,14,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position,15,', '||substr(column_name,1,30),NULL)) ||
  max(decode(column_position,16,', '||substr(column_name,1,30),NULL)) columns
  from user_ind_columns
  group by substr(table_name,1,30), substr(index_name,1,30) ) b
  where a.table_name = b.table_name (+)
  and b.columns (+) like a.columns || '%'
  外鍵的級聯刪除:在我們的案例中,已經使用“級聯刪除選項(On Delete Cascade)”創建了一些外鍵約束,這樣會引發死鎖問題,原因是在我們的刪除事務中,我們明確地刪除子表中的數據,然後再刪除主表中的數據,因此在子表上就已經存在一個鎖了,在刪除主表之前,我們想再刪除子表一次,因此導致的死鎖。
  爲了解決這個問題,我們移除了“級聯刪除選項(On Delete Cascade)”,這樣就修復了死鎖問題。


下面補充一些必備的基礎知識!

ORACLE鎖具體分爲以下幾類:
1.按用戶與系統劃分,可以分爲自動鎖與顯示鎖
自動鎖:當進行一項數據庫操作時,缺省情況下,系統自動爲此數據庫操作獲得所有有必要的鎖。
顯示鎖:某些情況下,需要用戶顯示的鎖定數據庫操作要用到的數據,才能使數據庫操作執行得更好,顯示鎖是用戶爲數據庫對象設定的。
2.按鎖級別劃分,可分爲共享鎖與排它鎖
共享鎖:共享鎖使一個事務對特定數據庫資源進行共享訪問——另一事務也可對此資源進行訪問或獲得相同共享鎖。共享鎖爲事務提供高併發性,但如拙劣的事務設計+共享鎖容易造成死鎖或數據更新丟失。
排它鎖:事務設置排它鎖後,該事務單獨獲得此資源,另一事務不能在此事務提交之前獲得相同對象的共享鎖或排它鎖。
3.按操作劃分,可分爲DML鎖、DDL鎖
+DML鎖又可以分爲,行鎖、表鎖、死鎖
-行鎖:當事務執行數據庫插入、更新、刪除操作時,該事務自動獲得操作表中操作行的排它鎖。
-表級鎖:當事務獲得行鎖後,此事務也將自動獲得該行的表鎖(共享鎖),以防止其它事務進行DDL語句影響記錄行的更新。事務也可以在進行過程中獲得共享鎖或排它鎖,只有當事務顯示使用LOCK TABLE語句顯示的定義一個排它鎖時,事務纔會獲得表上的排它鎖,也可使用LOCK TABLE顯示的定義一個表級的共享鎖(LOCK TABLE具體用法請參考相關文檔)。
-死鎖:當兩個事務需要一組有衝突的鎖,而不能將事務繼續下去的話,就出現死鎖。
如事務1在表A行記錄#3中有一排它鎖,並等待事務2在表A中記錄#4中排它鎖的釋放,而事務2在表A記錄行#4中有一排它鎖,並等待事務1在表A中記錄#3中排它鎖的釋放,事務1與事務2彼此等待,因此就造成了死鎖。死鎖一般是因拙劣的事務設計而產生。
死鎖只能使用SQL下:alter system kill session "sid,serial#";或者使用相關操作系統kill進程的命令,如UNIX下kill -9 sid,或者使用其它工具殺掉死鎖進程。
+DDL鎖又可以分爲:排它DDL鎖、共享DDL鎖、分析鎖
-排它DDL鎖:創建、修改、刪除一個數據庫對象的DDL語句獲得操作對象的 排它鎖。如使用alter table語句時,爲了維護數據的完成性、一致性、合法性,該事務獲得一排它DDL鎖。
-共享DDL鎖:需在數據庫對象之間建立相互依賴關係的DDL語句通常需共享獲得DDL鎖。
如創建一個包,該包中的過程與函數引用了不同的數據庫表,當編譯此包時,該事務就獲得了引用表的共享DDL鎖。
-分析鎖:ORACLE使用共享池存儲分析與優化過的SQL語句及PL/SQL程序,使運行相同語句的應用速度更快。一個在共享池中緩存的對象獲得它所引用數據庫對象的分析鎖。分析鎖是一種獨特的DDL鎖類型,ORACLE使用它追蹤共享池對象及它所引用數據庫對象之間的依賴關係。當一個事務修改或刪除了共享池持有分析鎖的數據庫對象時,ORACLE使共享池中的對象作廢,下次在引用這條SQL/PLSQL語句時,ORACLE重新分析編譯此語句。
4.內部閂鎖
內部閂鎖:這是ORACLE中的一種特殊鎖,用於順序訪問內部系統結構。當事務需向緩衝區寫入信息時,爲了使用此塊內存區域,ORACLE首先必須取得這塊內存區域的閂鎖,才能向此塊內存寫入信息。


關於數據庫死鎖的檢查方法

一、 數據庫死鎖的現象
程序在執行的過程中,點擊確定或保存按鈕,程序沒有響應,也沒有出現報錯。
二、 死鎖的原理
當對於數據庫某個表的某一列做更新或刪除等操作,執行完畢後該條語句不提
交,另一條對於這一列數據做更新操作的語句在執行的時候就會處於等待狀態,
此時的現象是這條語句一直在執行,但一直沒有執行成功,也沒有報錯。
三、 死鎖的定位方法
通過檢查數據庫表,能夠檢查出是哪一條語句被死鎖,產生死鎖的機器是哪一臺。
1)用dba用戶執行以下語句
select username,lockwait,status,machine,program from v$session where sid in
(select session_id from v$locked_object)
如果有輸出的結果,則說明有死鎖,且能看到死鎖的機器是哪一臺。字段說明:
Username:死鎖語句所用的數據庫用戶;
Lockwait:死鎖的狀態,如果有內容表示被死鎖。
Status: 狀態,active表示被死鎖
Machine: 死鎖語句所在的機器。
Program: 產生死鎖的語句主要來自哪個應用程序。
2)用dba用戶執行以下語句,可以查看到被死鎖的語句。
select sql_text from v$sql where hash_value in
(select sql_hash_value from v$session where sid in
(select session_id from v$locked_object))

四、 死鎖的解決方法
一般情況下,只要將產生死鎖的語句提交就可以了,但是在實際的執行過程中。用戶可
能不知道產生死鎖的語句是哪一句。可以將程序關閉並重新啓動就可以了。
 經常在Oracle的使用過程中碰到這個問題,所以也總結了一點解決方法。

  1)查找死鎖的進程:

sqlplus "/as sysdba" (sys/change_on_install)
SELECT s.username,l.OBJECT_ID,l.SESSION_ID,s.SERIAL#,
l.ORACLE_USERNAME,l.OS_USER_NAME,l.PROCESS
FROM V$LOCKED_OBJECT l,V$SESSION S WHERE l.SESSION_ID=S.SID;

  2)kill掉這個死鎖的進程:

  alter system kill session ‘sid,serial#’; (其中sid=l.session_id)

  3)如果還不能解決:

select pro.spid from v$session ses,
v$process pro where ses.sid=XX and
ses.paddr=pro.addr;

  其中sid用死鎖的sid替換:

exit
ps -ef|grep spid

  其中spid是這個進程的進程號,kill掉這個Oracle進程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章