ORACLE中死鎖


ORACLE中死鎖的知識點總結

 

死鎖的概念

 

    什么是死鎖呢? 其實我們生活中也有很多類似死鎖的例子。 我先舉一個生活中的例子:過年回家,父親買了一把水彈槍,兒子和侄子爭搶着要先玩,誰也不讓誰,拆開包裝后,一個搶了槍, 一個逮住了子彈和彈夾。兩個都爭着要先玩,但是都互不相讓。結果兩個人都玩不了。如果兒子要先玩,就必須讓侄子把子彈和彈夾給他,如果侄子要先玩,就必須讓兒子把槍給侄子。他們就這樣對峙了十幾分鍾,互不相讓。 我出來調停,讓兒子把槍先給侄子玩,每個人玩十分鍾。然后兩個人開開心心一起玩起來。其實這就是一個活生生的死鎖(Dead Lock)的例子。

 

   我們再來看看數據庫死鎖的概念, 所謂死鎖,是指兩個會話,每個會話都持有另外一個會話想要的資源,因爭奪資源而造成的一種互相等待的現象,此時就會出現死鎖,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。Oracle對於死鎖采取的策略是回滾其中一個事務,讓另外一個事務順利進行。

 

   英文關於deadlock的概念如下:

 

A deadlock occurs when a session (A) wants a resource held by another session (B) , but that session also wants a resource held by the first session (A). There can be more than 2 sessions involved but the idea is the same.

 

   

死鎖的模擬

 

 

   上面了解了死鎖的概念,接下來,我們先人工構造一個簡單的死鎖(Dead Lock)案例來加深理解一下死鎖(Dead Lock),如下所示,我們先准備測試案例使用的表和數據,測試環境為Oracle Database 10g Release 10.2.0.5.0

 

SQL> create table dead_lock_test( id number(10), name varchar2(32));
 
Table created.
 
SQL> insert into dead_lock_test values(101, 'kerry');
 
1 row created.
 
SQL> insert into dead_lock_test values(102, 'ken');
 
1 row created.
 
SQL> commit;
 
Commit complete.
 
SQL> 

 

 

在會話1(SID為788)中執行下面SQL語句:

 

 

SQL> show user;
USER is "TEST"
SQL> select * from v$mystat where rownum=1; 
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       788          0          1
 
SQL> update dead_lock_test set name='kerry1_101' where id=101;
 
1 row updated.
 
SQL> 

 

 

 

 

 

然后在會話2(SID為770)中執行下面SQL語句:

 

SQL> show user;
USER 為 "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       770          0          1
 
SQL> update dead_lock_test set name='kerry2_102' where id=102;
 
已更新 1 行。
 
SQL> update dead_lock_test set name='kerry2_101' where id=101;

 

 

 

 

 

如上所示,會話2(SID為770)更新id=101這條記錄時,會話被阻塞了。然后我們在會話1(SID為788)中執行下面SQL語句:

 

SQL>  update dead_lock_test set name='kerry1_102' where id=102;

 

 

 

此時你會立馬看到會話2(SID為770)出現ORA-00060錯誤,如下所示:

 

 

 

 

如果對上面的操作過程的流程有點不直觀,那么可以參下面表格:

 

 

 

     

 

當然,如果你以下面這樣的順序更新,那么會話1就會出現ORA-0060的錯誤,會話1會被當做犧牲的會話進行回滾。

 

 

 

 

                                

 

 

此時在告警日志中就會出現trc文件。注意RAC環境和單機環境稍有不同。在RAC環境中,是由LMD(Lock Manager Daemon)進程統一管理各個節點之間的鎖資源的,所以,RAC環境中trace文件是由LMD進程來生成的。

 

Tue Mar 28 15:36:30 CST 2017

ORA-00060: Deadlock detected. More info in file /u01/app/oracle/admin/SCM2/bdump/scm2_s000_15815.trc

 

trace文件的部分內容如下所示:

 

*** 2017-03-28 15:36:30.917
*** ACTION NAME:() 2017-03-28 15:36:30.917
*** MODULE NAME:(SQL*Plus) 2017-03-28 15:36:30.917
*** SERVICE NAME:(SCM2) 2017-03-28 15:36:30.917
*** SESSION ID:(770.8) 2017-03-28 15:36:30.917
DEADLOCK DETECTED ( ORA-00060 )
[Transaction Deadlock]
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
TX-0006002e-001e409f        15     770     X             16     788           X
TX-0007002c-001f6346        16     788     X             15     770           X
session 770: DID 0001-0010-00000002     session 788: DID 0001-000F-00000001
session 788: DID 0001-000F-00000001     session 770: DID 0001-0010-00000002
Rows waited on:
Session 788: obj - rowid = 00094900 - AACUkAABEAACLUeAAB
  (dictionary objn - 608512, file - 68, block - 570654, slot - 1)
Session 770: obj - rowid = 00094900 - AACUkAABEAACLUeAAA
  (dictionary objn - 608512, file - 68, block - 570654, slot - 0)
Information on the OTHER waiting sessions:
Session 788:
  sid: 788 ser: 9 audsid: 201878652 user: 132/TEST
    flags: (0xe1) USR/- flags_idl: (0x1) BSY/-/-/-/-/-
    flags2: (0x8)
  pid: 16 O/S info: user: oracle, term: UNKNOWN, ospid: 15817
    image: oracle@getlnx14uat.xxxx.com (S001)
  O/S info: user: oracle, term: pts/2, ospid: 23047, machine: DB-Server.localdomain
            program: sqlplus@DB-Server.localdomain (TNS V1-V3)
  application name: SQL*Plus, hash value=3669949024
  Current SQL Statement:
  update dead_lock_test set name='kerry1_102' where id=102
End of information on OTHER waiting sessions.
Current SQL statement for this session:
update dead_lock_test set name='kerry2_101' where id=101
===================================================

 

 

死鎖的檢測

 

關於死鎖的檢測,對於單實例來說,基本上秒級完成,對於RAC環境,Oracle 10g基本上是1分鍾, Oracle 11g是10秒,這個是通過隱含參數_lm_dd_interval控制的。這個參數可以修改,但是不建議修改。

 

COL NAME FOR A32;
COL KSPPDESC FOR A32;
COL KSPPSTVL FOR A32;
SELECT A.INDX,
        A.KSPPINM NAME,
        A.KSPPDESC,
       B.KSPPSTVL
FROM   X$KSPPI  A,
        X$KSPPCV B
WHERE  A.INDX = B.INDX
     AND LOWER(A.KSPPINM) LIKE  LOWER('%&PARAMETER%');

 

 

 

 

 

 

 

 

死鎖的分析(DeadLock Troubleshooting)

 

 

以上面的例子來說,數據庫一旦出現死鎖,立馬會在告警日志里面生成這樣一條記錄ORA-00060: Deadlock detected. More info in file xxxxx,那么從trc文件能分析出什么信息呢? 下面我們以上面的例子來簡單分析一下

 

 

 

其實trc文件里面最重要、最有用的信息是Deadlock graph。從這部分,我們可以分析得到下面一些有用信息:

 

 

1: 產生死鎖的兩個會話信息

 

 

Deadlock graph:

                       ---------Blocker(s)--------  ---------Waiter(s)---------

Resource Name          process session holds waits  process session holds waits

TX-0006002e-001e409f        15     770     X             16     788           X

TX-0007002c-001f6346        16     788     X             15     770           X

session 770: DID 0001-0010-00000002     session 788: DID 0001-000F-00000001

session 788: DID 0001-000F-00000001     session 770: DID 0001-0010-00000002

 

從上面可以看到Blocker(s)與Waiter(s)的相關信息

 

Resource Name :  被持有或等待的鎖資源名字

          鎖資源名字由三部分組成 Type-ID1-ID2,ID1和ID2代表的意思由鎖類型決定。

         具體可以參考v$lock_type

process   :  V$PROCESS.PID

session   :  V$SESSION.SID

holds    :  鎖持有的模式(Mode the lock is held in)

waits    :  鎖等待的模式(Mode the lock is requested in (waiting for))

 

解讀以上死鎖的案例:

 

SID 770 (Process 15) 以排它模式持有鎖:TX-0006002e-001e409f ,以排它模式請求鎖:TX-0007002c-001f6346。

SID 788 (Process 16) 以排它模式持有鎖:TX-0007002c-001f6346 ,以排它模式請求鎖:TX-0006002e-001e409f。

 

這一段可以看到,778 阻塞了770, 然后770又阻塞了778 剛好構成了死鎖的條件。這里要看生成的記錄是兩行還是一行,是TX還是TM,如果只有一行那么說明是同一個SESSION,可能是自治事務引起的死鎖。

 

 

2:死鎖發生在那個對象?

 

Rows waited on:

Session 788: obj - rowid = 00094900 - AACUkAABEAACLUeAAB

  (dictionary objn - 608512, file - 68, block - 570654, slot - 1)

Session 770: obj - rowid = 00094900 - AACUkAABEAACLUeAAA

  (dictionary objn - 608512, file - 68, block - 570654, slot - 0)

 

 

 

 

3:會話的的機器、應用程序等信息

 

Session 788:

  sid: 788 ser: 9 audsid: 201878652 user: 132/TEST

    flags: (0xe1) USR/- flags_idl: (0x1) BSY/-/-/-/-/-

    flags2: (0x8)

  pid: 16 O/S info: user: oracle, term: UNKNOWN, ospid: 15817

    image: oracle@xxxxxx.xxxx.com (S001)

  O/S info: user: oracle, term: pts/2, ospid: 23047, machine: DB-Server.localdomain

            program: sqlplus@DB-Server.localdomain (TNS V1-V3)

  application name: SQL*Plus, hash value=3669949024

  Current SQL Statement:

  update dead_lock_test set name='kerry1_102' where id=102

End of information on OTHER waiting sessions.

Current SQL statement for this session:

update dead_lock_test set name='kerry2_101' where id=101

 

從上面我們可以看到會話788是從機器DB-Server.localdomain上的SQL*Plus應用程序發出的SQL,如果是正式環境,你會看到相關的機器和應用程序名稱。這個會話最后執行的SQL語句為update dead_lock_test set name='kerry1_102' where id=102 。

另外一個會話執行的最后語句為update dead_lock_test set name='kerry2_101' where id=101,但是如何找到對應的機器、應用程序信息呢?

 

如下截圖所示,我們在PROCESS STATE部分,找到對應的SID 770的事務,可以看到user,term、machine、program信息。剩下的事情,就是你和開發人員分析腳本,縷清細節,然后如何避免死鎖的問題。

 

 

 

 

 

 

死鎖的分類

 

死鎖如何分類呢?在Metalink上這篇文章中"How to Identify ORA-00060 Deadlock Types Using Deadlock Graphs in Trace (文檔 ID 1507093.1)"有關於死鎖的分類:如下所示:

 

 

"Key Signature"

Lock Type

Requested
Lock Mode

Deadlock Graph

Likely
Deadlock Type

Comments

Type TX Lock Requesting Mode X (6)

TX

X(6)

TX X
TX
 
X

Application

TX Lock Held in Mode X (6) Requesting Mode X (6)

Type TM Lock Requesting Mode SSX (5)

TM

SSX (5)

TM SX SSX SX SSX
TM
 SX SSX 
SX SSX

Missing Index on Foreign Key (FK) Constraint

TM  Lock Held in Mode SX (3) Held SSX (5) Requested

Type TX Lock Requesting Mode S(4)

TX

S(4)

TX S
TX
 
S

Insufficient Interested Transaction List (ITL) Provision
OR
Bitmap Index
OR
PK/UK Index

TX Lock Held in Mode X (6) Requesting Mode S (4)

ITL, Bitmap Index and PK/UK Index Signatures are the Same. Further Investigation will be required to identify absolute cause

Type TX Lock Requesting Mode X (6)
Single Row in Deadlock Graph

TX

X(6)

TX X
Single Row in Deadlock Graph

Self Deadlock
OR
Autonomous Transaction Self Deadlock

This looks the same as a standard application deadlock except that there is only a single row in the deadlock graph.

Type UL Lock in Deadlock Graph

UL

ANY

UL ? ?
?

Application Deadlock Featuring User Defined Locks

This is very similar to the standard application deadlock except that it features User Defined Locks

 

 

 

 

李華榮這篇博客Oracle死鎖(DeadLock)的分類及其模擬里面對死鎖進行了一個分類,個人覺得是一個通俗、很贊的一個死鎖分類。本文很多地方也是參考、借鑒他博客的內容。

 

 

 

 

 

 

那么我們接下來看看這些死鎖產生的場景,並進行一些分析,很多知識點都是參考Metalink上的一些知識點。

 

 

 

 

1:應用程序死鎖(Application Deadlock

 

 

其實最上面那個死鎖的例子,就屬於Application Deadlock,這個Application Deadlock是發生在同一個表,下面我們介紹一下Application Deadlock發生在兩個表之間不同順序相互更新操作引起的死鎖。下面開始我們的實驗。創建兩個測試表,並初始化數據。

 

 

SQL> select * from v$version;       
 
BANNER
----------------------------------------------------------------
Oracle Database 10g Release 10.2.0.5.0 - 64bit Production
PL/SQL Release 10.2.0.5.0 - Production
CORE    10.2.0.5.0      Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production
 
SQL> create table lock_test_one(name varchar(32));
 
Table created.
 
SQL> create table lock_test_two(name varchar(32));
 
Table created.
 
SQL> insert into lock_test_one values('aaaaaaaa');
 
1 row created.
 
SQL> commit;
 
Commit complete.
 
SQL> insert into lock_test_two values('bbbbbbbb');
 
1 row created.
 
SQL> commit;
 
Commit complete.

 

在會話1(SID=685)里更新lock_test_one的記錄,但是不提交更新(下面是一個模擬死鎖出現的過程)

 

 

SQL> show user;
USER is "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       685          0          1
 
SQL> update lock_test_one set name='cccccccc' where name='aaaaaaaa';
 
1 row updated.

 

 

在會話2(SID=719)里面更新lock_test_two的記錄,也不提交更新:

 

 

SQL> show user;
USER 為 "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       719          0          1
 
SQL> update lock_test_two set name='dddddddd' where name='bbbbbbbb';
 
已更新 1 行。

 

 

接下來在會話1(SID=685)里面更新lock_test_two,你會發現會話1被回話2阻塞了(開啟另外一個會話查詢,就會發現阻塞信息,如下截圖所示)

 

 

SQL> update lock_test_two set name='eeeeeeee' where name='bbbbbbbb';

 

 

 

 

 

接下來在會話2里面更新lock_test_one, 模擬死鎖就會出現了

 

 

SQL> update lock_test_one set name='ffffffff' where name='aaaaaaaa';

 

 

 

最后,在會話2(SID=719)里面執行上面語句后,你會發現會話1立即就會出現ORA-00060的死鎖錯誤信息。

 

 

 

 

 

 

 

那么接下來我們來分析一下死鎖日志,如下所示,死鎖有兩行記錄,發生在兩個會話之間,可以判斷為Application Deadlock. 從objn的值來看,

 

有兩個值,意味着兩個表之間不同順序相互更新操作引起了死鎖,典型的應用程序死鎖有下面特征:

 

    1:Deadlock graph中的記錄多於1行。

    2:Deadlock graph中至少有一行鎖類型是TX-X-X

 

 

 

SQL> col object_name for a32;
SQL> col object_type for a32;
SQL> select object_name, object_type from dba_objects
  2  where object_id=608789;
 
OBJECT_NAME                      OBJECT_TYPE
-------------------------------- --------------------------------
LOCK_TEST_ONE                    TABLE
 
SQL> select object_name, object_type from dba_objects
  2  where object_id=608790;
 
OBJECT_NAME                      OBJECT_TYPE
-------------------------------- --------------------------------
LOCK_TEST_TWO                    TABLE
 
SQL> 

 

還有其它一些有用的信息,例如你想知道死鎖發生在哪一行,那么可以通過rowid等位到死鎖發生在哪一行。

 

 

 

Rows waited on:

Session 719: obj - rowid = 00094A15 - AACUoVABEAACLUnAAB

  (dictionary objn - 608789, file - 68, block - 570663, slot - 1)

Session 685: obj - rowid = 00094A16 - AACUoWABEAACLUvAAA

  (dictionary objn - 608790, file - 68, block - 570671, slot - 0)

 

 

 

SQL> select * from lock_test_one where rowid='AACUoVABEAACLUnAAB';
 
NAME
--------------------------------
cccccccc
 
SQL> select * from lock_test_two where rowid='AACUoWABEAACLUvAAA';
 
NAME
--------------------------------
bbbbbbbb

 

 

  

 

 

 

2:缺少外鍵索引引起的死鎖。

 

 

創建表dead_lock_parent與dead_lock_foreign,兩者存在主外鍵關系,分布插入兩條測試數據: 

 

 

SQL> create table dead_lock_parent( id number primary key, name varchar2(32));
 
Table created.
 
SQL> create table dead_lock_foreign(fid  number, fname varchar2(32), foreign key(fid) references dead_lock_parent);
 
Table created.
 
SQL> insert into dead_lock_parent values( 1, 'kerry');
 
1 row created.
 
SQL> insert into dead_lock_foreign values(1, 'kerry_fk');  
 
1 row created.
 
SQL> insert into dead_lock_parent values(2, 'jimmy');
 
1 row created.
 
SQL> insert into dead_lock_foreign values(2, 'jimmy_fk');
 
1 row created.
 
SQL> commit;
 
Commit complete.
 
SQL> 

 

1:在會話1(會話ID為789)里面執行下面SQL語句:

 

 

 

SQL> show user;
USER 為 "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       789          0          1
 
SQL> delete from dead_lock_foreign where fid=1;
 
已刪除 1 行。

 

 

 

2:在會話2(會話ID為766)里面執行下面SQL語句:

 

 

SQL> show user;
USER is "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       766          0          1
 
SQL> delete from dead_lock_foreign where fid=2;
 
1 row deleted.

 

 

 

3:接着在會話1(會話ID為789)里執行刪除dead_lock_parent中id為1的記錄:

 

 

SQL> delete from dead_lock_parent where id=1;

 

 

此時你會發現會話被阻塞了,我們可以用下面SQL查詢具體的阻塞信息。

 

COL MODE_HELD FOR A14;
COL LOCK_TYPE FOR A8;
COL MODE_REQUESTED FOR A10;
COL OBJECT_TYPE FOR A14;
COL OBJECT_NAME FOR A20;
SELECT LK.SID,
       DECODE(LK.TYPE,
              'TX',
              'Transaction',
              'TM',
              'DML',
              'UL',
              'PL/SQL User Lock',
              LK.TYPE) LOCK_TYPE,
       DECODE(LK.LMODE,
              0,
              'None',
              1,
              'Null',
              2,
              'Row-S (SS)',
              3,
              'Row-X (SX)',
              4,
              'Share',
              5,
              'S/Row-X (SSX)',
              6,
              'Exclusive',
              TO_CHAR(LK.LMODE)) MODE_HELD,
       DECODE(LK.REQUEST,
              0,
              'None',
              1,
              'Null',
              2,
              'Row-S (SS)',
              3,
              'Row-X (SX)',
              4,
              'Share',
              5,
              'S/Row-X (SSX)',
              6,
              'Exclusive',
              TO_CHAR(LK.REQUEST)) MODE_REQUESTED, 
       OB.OBJECT_TYPE,
       OB.OBJECT_NAME,
       LK.BLOCK,
       SE.LOCKWAIT
  FROM V$LOCK LK, DBA_OBJECTS OB, V$SESSION SE
 WHERE LK.TYPE IN ('TM', 'UL')
   AND LK.SID = SE.SID
   AND LK.ID1 = OB.OBJECT_ID(+)
 AND SE.SID IN (766,789)
 ORDER BY SID;

 

 

 

 

 

 

接着在會話2(會話ID為766)里面執行下面SQL,刪除主表中id=2的記錄

 

 

SQL> delete from dead_lock_parent where id=2;

 

 

你會發現會話1(會話ID為789)就會出現Deadlock

 

 

 

 

 

在trace文件里面,你能看到如下一些信息:

 

 

 

 

特征:

 

1:在Deadlock Graph里面有多行記錄。

2:在Deadlock Graph至少一行的鎖類型為TM,並且Waiter等待類型為SSX"(Share Row-eXclusive: Mode 5)

 

 

此時需要去查找對應的主外鍵約束的表,我們可以在PROCESS STATE里面找到對應object的object_id, 注意此時這個object_id不是十進制數,而是十六進制數。如下所示:

 

 

 

SQL> col object_name for a32;
SQL> select object_id, object_name from dba_objects where object_name =upper('dead_lock_foreign');
 
 OBJECT_ID OBJECT_NAME
---------- --------------------------------
    608975 DEAD_LOCK_FOREIGN
 
SQL> select to_char(608975,'xxxxx') from dual;
 
TO_CHA
------
 94acf

 

 

 

 

 

 

 

找到對應的外鍵表,然后添加索引,然后你再重復上面操作,你就會發現死鎖不會出現了。

 

 

SQL> create index idx_dead_lock_foreign_n1 on dead_lock_foreign(fid);

 

Index created.

 

 

 

關於外鍵缺少索引,可以詳細參考我這篇博客"ORACLE中關於外鍵缺少索引的探討和總結"

 

 

 

3:主鍵或唯一索引更新引起的死鎖

 

 

SQL>  create table dead_lock_primary(id number(10) primary key, name varchar2(32));

 

Table created.

 

 

首先在會話(會話ID為930)下執行下面SQL

 

SQL> show user;
USER is "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       930          0          1
 
SQL> insert into dead_lock_primary values(2, 'jimmy');
 
1 row created.

 

 

然后在會話2(會話ID為926)里下面執行下面SQL

 

 

SQL> show user;
USER 為 "TEST"
SQL> select * from v$mystat where rownum =1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       926          0          1
 
SQL> insert into dead_lock_primary values(1, 'kerry');
 
已創建 1 行。

 

 

 

然后在會話1(會話ID為930)下插入id=1的記錄。

 

 

SQL> insert into dead_lock_primary values(1, 'kkk');

 

 

此時在會話3下執行下面SQL,你會看到會話2被阻塞了, 如下所示:

 

 

 

SQL> @get_locked_objects_rpt.sql
Enter value for 1: 5
old  42:    AND locks_t.blocked_secs > &1
new  42:    AND locks_t.blocked_secs > 5
========= $Revision: 1.4 $ ($Date: 2013/09/16 13:15:22 $) ===========
Locked object : MONI_DDL_LOG
Locked row#   : AACg2tAAsAAAdaaAAA
Blocked for   : 238 seconds
Blocker info. : TEST@xxxx\GET253194(SID=926) [sqlplus.exe/PID=14144:10392]
Blocked info. : TEST@DB-Server.localdomain(SID=930)
[sqlplus@DB-Server.localdomain (TNS V1-V3)/PID=6806]
Blocked SQL   : insert into dead_lock_primary values(1, 'kkk')
Found 1 blocked session(s).
Disconnected from Oracle Database 10g Release 10.2.0.5.0 - 64bit Production

 

接着在會話2(會話ID為926)下執行下面SQL,死鎖立馬出現:

 

 

SQL> insert into dead_lock_primary values(2, 'jimmy');

 

 

 

 

 

 

 

 

 

特征:

 

·         More than one row in the deadlock graph

·         At Least 1 Row in the Deadlock graph is "TX X S" 

·         i.e. The Lock type is TX and the holder holds this in "X" (eXclusive: Mode 6) and waits for nothing. The waiter waits for "S" (Share:Mode 4) and waits for nothing

·         At least one of the Involved Objects is a Primary or Unique Key Index

·         Data is being Inserted or changed in an inconsistent order

 

 

4:位圖索引更新引起的死鎖。

 

 

下面我們來看看位圖索引的更新為什么會導致死鎖。我們先構造下面測試所需的例子:

 

 

SQL>  create table dead_lock_bitmap_index
  2  (
  3     id number(10),
  4     name varchar(30),
  5     sex number(1)
  6  );
 
Table created.
 
SQL> insert into dead_lock_bitmap_index 
  2  values(1000, 'kerry', 1);
 
1 row created.
 
SQL> insert into dead_lock_bitmap_index
  2  values(1001, 'Merry', 0);
 
1 row created.
 
SQL> insert into dead_lock_bitmap_index
  2  values(1002, 'Richard', 1);
 
1 row created.
 
SQL> insert into dead_lock_bitmap_index
  2  values(1003, 'Ken', 0);
 
1 row created.
 
SQL> commit;
 
Commit complete.
 
SQL> 
SQL>  create bitmap index idx_dead_lock_bitmap_index on dead_lock_bitmap_index(sex);
 
Index created.
 
SQL> 

 

在會話1(會話ID為14)中執行下面SQL,此時sex=1的所有行都會被鎖定。

 

 

SQL> show user;
USER is "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
        14          0          0
 
SQL> update dead_lock_bitmap_index set sex=3 where id=1000 and sex=1;
 
1 row updated.

 

 

 

在會話2(會話ID為69)中執行下面SQL,此時sex=0的所有行都會被鎖定

 

 

SQL> show user;
USER 為 "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
        69          0          0
 
SQL> update dead_lock_bitmap_index set sex=4 where id=1001 and sex=0;
 
已更新 1 行。

 

 

 

在會話1(會話ID為14)中執行下面SQL,此時,這個SQL語句將會被阻塞

 

 

SQL> update dead_lock_bitmap_index set sex=4 where  id=1003 and sex=0;

 

 

 

新建會話,執行下面SQL語句查看具體阻塞情況,如下所示:

 

 

 

SQL> COL USERNAME FOR A14;
SQL> COL MACHINE  FOR A26;
SQL> COL OBJECT_NAME FOR A26;
SQL> SELECT S.SID                             SID, 
  2           S.USERNAME                      USERNAME, 
  3           S.MACHINE                       MACHINE, 
  4           L.TYPE                          TYPE, 
  5           O.OBJECT_NAME                   OBJECT_NAME, 
  6           DECODE(L.LMODE, 0, 'None', 
  7                           1, 'Null', 
  8                           2, 'Row Share', 
  9                           3, 'Row Exlusive', 
 10                           4, 'Share', 
 11                           5, 'Sh/Row Exlusive', 
 12                           6, 'Exclusive')   lmode, 
 13      DECODE(L.REQUEST, 0, 'None', 
 14                             1, 'Null', 
 15                             2, 'Row Share', 
 16                             3, 'Row Exlusive', 
 17                             4, 'Share', 
 18                             5, 'Sh/Row Exlusive', 
 19                             6, 'Exclusive') request, 
 20           L.BLOCK                           BLOCK 
 21    FROM   V$LOCK L, 
 22           V$SESSION S, 
 23           DBA_OBJECTS O 
 24    WHERE  L.SID = S.SID 
 25           AND USERNAME != 'SYSTEM' 
 26           AND O.OBJECT_ID(+) = L.ID1 
 27           AND S.SID IN ( 14,69) 
 28    ORDER  BY S.SID; 
 
       SID USERNAME       MACHINE                    TY OBJECT_NAME                LMODE           REQUEST              BLOCK
---------- -------------- -------------------------- -- -------------------------- --------------- --------------- ----------
        14 TEST           myvlnx14uat.localt.com     TM DEAD_LOCK_BITMAP_INDEX     Row Exlusive    None                     0
        14 TEST           myvlnx14uat.localt.com     TX                            None            Share                    0
        14 TEST           myvlnx14uat.localt.com     TX MRAC_OLAP2_AW_DIMENSIONS_V Exclusive       None                     0
        14 TEST           myvlnx14uat.localt.com     AE ORA$BASE                   Share           None                     0
        69 TEST           xxxx\GET253194             TM DEAD_LOCK_BITMAP_INDEX     Row Exlusive    None                     0
        69 TEST           xxxx\GET253194             TX                            Exclusive       None                     1
        69 TEST           xxxx\GET253194             AE ORA$BASE                   Share           None                     0
 
7 rows selected.
 
SQL> 

 

 

 

在會話2(會話ID為69)中執行下面SQL,此時,在會話1(會話ID為14)中,你就會立馬看見死鎖出現:

 

SQL> update dead_lock_bitmap_index set sex=3 where id=1002 and sex=1;

 

 

死鎖情況如下所示:

 

 

SQL> show user;
USER is "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
        14          0          0
 
SQL> update dead_lock_bitmap_index set sex=3 where id=1000 and sex=1;
 
1 row updated.
 
SQL> 
SQL> update dead_lock_bitmap_index set sex=4 where  id=1003 and sex=0;
update dead_lock_bitmap_index set sex=4 where  id=1003 and sex=0
       *
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
 
 
SQL> 

 

 

有位圖索引存在的表上面,非常容易就引發阻塞與死鎖。這個阻塞不是發生在表上面,而是發生在索引上。因為位圖索引鎖定的范圍遠遠比普通的b-tree索引鎖定的范圍大。

 

 

特征:

 

    位圖索引更新引起的死鎖的特征跟主鍵或唯一索引更新引起的死鎖的特征有部分是類似的。

 

 

The typical deadlock graph for an issue caused by Bitmap Indexes locking multiple contending rows is characterised by the following:

More than one row in the deadlock graph

 

    At Least 1 Row in the Deadlock graph is "TX X S" i.e. The Lock type is TX and the holder holds this in "X" (eXclusive: Mode 6) and waits for nothing. The waiter waits for "S" (Share:Mode 4) and waits for nothing

    At Least one of the Involved Objects is a Bitmap Indexes

 

 

COLUMN OBJECT_NAME FORMAT A15
COLUMN OWNER FORMAT A10
SELECT O.OBJECT_ID,
  O.OWNER,
  O.OBJECT_NAME,
  I.INDEX_TYPE
FROM DBA_OBJECTS O,
  DBA_INDEXES I
WHERE I.OWNER(+)   =O.OWNER
AND I.INDEX_NAME(+)=O.OBJECT_NAME
AND O.OBJECT_TYPE  = 'INDEX'
AND O.OBJECT_ID    =  &OBJECT_ID 
/

 

 

 

 

 

另外關於位圖索引的選擇是有一些場景。謹慎選擇:

 

 

 

樹索引更適合索引動態表的 OLTP 環境,而位圖索引更適合在大型靜態表上使用復雜查詢的數據倉庫環境

 

位圖索引被存儲為壓縮的索引值,其中包含了一個范圍內的ROWID,因此ORACLE必須針對一個給定值鎖定所有范圍內的ROWID,不支持行級別的鎖定。

 

位圖索引被存儲為壓縮的索引值,其中包含了一個范圍內的ROWID,因此ORACLE必須針對一個給定值鎖定所有范圍內的ROWID,不支持行級別的鎖定。 

  

    換一種描述方法:使用位圖索引時,一個鍵指向多行(成百上千),如果更新一個位圖索引鍵,會同時將其他行對應位圖索引字段進行鎖定! 

 

  較之B-Tree索引優點: 

  位圖以一種壓縮格式存放,因此占用的磁盤空間比B-Tree索引要小得多 

 

  較之B-Tree索引缺點: 

  這種鎖定的代價很高,會導致一些DML語句出現鎖等待,嚴重影響插入、更新和刪除的效率,對於高並發的系統不適用。 

 

  位圖索引使用原則: 

  位圖索引主要用於決策支持系統或靜態數據,不支持索引行級鎖定。

 

 

5:自治事務引發的死鎖

 

  ORACLE自制事務是指的存儲過程和函數可以自己處理內部事務不受外部事務的影響,用PRAGMA AUTONOMOUS_TRANSACTION來聲明,要創建一個自治事務,您必須在匿名塊的最高層或者存儲過程、函數、數據包或觸發的定義部分中,使用PL/SQL中的PRAGMA AUTONOMOUS_TRANSACTION語句。在這樣的模塊或過程中執行的SQL語句都是自治的。

 

我們先准備測試環境,如下所示。

 

 

Connected to:
Oracle Database 10g Release 10.2.0.5.0 - 64bit Production
 
SQL> create table pat_deadlock_test(id number, name varchar(32));
 
Table created.
 
SQL> insert into pat_deadlock_test   
  2  select 1001, 'kerry' from dual union all
  3  select 1002, 'ken'   from dual;
 
2 rows created.
 
SQL> commit;
 
Commit complete.
 
SQL> --創建存儲過程,也可以用觸發器、匿名塊等。
SQL> create or replace procedure prc_test_dead_lock as 
  2  pragma autonomous_transaction;
  3  begin
  4       update pat_deadlock_test set name='richard' where id=1002;
  5       commit;
  6  end;
  7  /
 
Procedure created.
 
SQL> 
 

 

接下來演示一下自治事務發生死鎖的情況。如下所示

 

SQL> show user;
USER is "TEST"
SQL> select * from v$mystat where rownum=1;
 
       SID STATISTIC#      VALUE
---------- ---------- ----------
       837          0          1
 
SQL> update pat_deadlock_test set name='kkk' where id=1002;
 
1 row updated.
 
SQL> exec prc_test_dead_lock;
BEGIN prc_test_dead_lock; END;
 
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
ORA-06512: at "TEST.PRC_TEST_DEAD_LOCK", line 4
ORA-06512: at line 1

 

 

檢查告警日志,就能發現告警日志里面提示死鎖生成了trace文件 ORA-00060: Deadlock detected. More info in file /u01/app/oracle/admin/SCM2/udump/scm2_ora_30818.trc.

 

 

 

 

 

 

 

自治事務引起死鎖的特征:

 

  This type of deadlock occurs when a single session manages to deadlock itself and is characterised by the following:

·         Only one row in the deadlock graph

·         The Row in the Deadlock graph is "TX X X" i.e. The Lock type is TX and the holder holds this in "X" (eXclusive: Mode 6) and waits for nothing. The waiter waits in "X" (eXclusive: Mode 6) and holds nothing.

·         The session is NOT involved in an autonomous transaction

 

      You can determine whether the session is involved in an autonomous transaction by searching for an "Autonomous Transaction Frames" section in the trace file:

 

 

 

6ITL死鎖

 

 

ITL概念

 

ITL(Interested Transaction List)是Oracle數據塊內部的一個組成部分,用來記錄該塊所有發生的事務,有的時候也叫ITL槽位。如果一個事務一直沒有提交,那么,這個事務將一直占用一個ITL槽位,ITL里面記錄了事務信息、回滾段的入口和事務類型等等。如果這個事務已經提交,那么,ITL槽位中還保存有這個事務提交時候的SCN號。ITL的個數受表的存儲參數INITRANS控制,在一個塊內部,默認分配了2個ITL的個數,如果這個塊內還有空閑空間,那么Oracle是可以利用這些空閑空間再分配ITL。如果沒有了空閑空間,那么,這個塊因為不能分配新的ITL,所以,就可能發生ITL等待。如果在並發量特別大的系統中,那么最好分配足夠的ITL個數,或者設置足夠的PCTFREE,保證ITL能擴展,但是PCTFREE有可能是被行數據給消耗掉的,例如UPDATE,所以,也有可能導致塊內部的空間不夠而導致ITL等待,出現了ITL等待就可能導致ITL死鎖。

 

本來想在此總結一下ITL死鎖,不過個人看了李華榮寫的Oracle死鎖(DeadLock)的分類及其模擬中ITL死鎖,感覺他已經把別人想寫的都寫完了,所以,如果對TIL死鎖部分感興趣,就參考他的博客吧。

 

 

The typical ITL deadlock graph is characterised by the following:

  • More than one row in the deadlock graph
  • At Least 1 Row in the Deadlock graph is "TX X S"?
    i.e. The Lock type is TX and the holder holds this in "X" (eXclusive: Mode 6) and waits for nothing. The waiter waits for "S" (Share:Mode 4) and waits for nothing
  • None of the Involved Objects are Bitmap Indexes

Process state is likely to contain waiting for 'enq: TX - allocate ITL entry' as the current wait or as a previous wait in the recent wait stack:

 

 

 

避免發生死鎖

 

 數據庫的死鎖產生是有一定條件的。死鎖產生的四個必要條件:

 

1)Mutual exclusion(互斥):資源不能被共享,只能由一個進程使用。

2)Hold and wait(請求並保持):已經得到資源的進程可以再次申請新的資源。

3)No pre-emption(不可剝奪):已經分配的資源不能從相應的進程中被強制地剝奪。

4)Circular wait(循環等待條件):系統中若干進程組成環路,該環路中每個進程都在等待相鄰進程正占用的資源。

 

 

其實死鎖的產生都是應用程序設計問題導致,我們一般通過分析trace文件分析死鎖產生的原因后,就可以去破壞死鎖產生的條件,來防止死鎖產生,從而解決死鎖問題。例如,上面案例中,調整更新表的順序,外鍵增加索引等等。

 

 

 

 

 

參考資料:

 

 

http://www.cnblogs.com/lhrbest/p/6005702.html

http://www.cnblogs.com/lhrbest/articles/5388514.html

https://support.oracle.com/epmos/faces/DocumentDisplay?_afrLoop=253326631915825&id=62365.1&displayIndex=2&_afrWindowMode=0&_adf.ctrl-state=aoa3r7ym5_105


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM