【锁】Oracle死锁(DeadLock)的分类及其模拟

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【锁】Oracle死锁(DeadLock)的分类及其模拟 1  BLOG文档结构图   2  前言部分 2.

】Oracle死锁(DeadLock)的分类及其模拟

1  BLOG文档结构图

wpsA550.tmp 

2  前言部分

2.1  导读和注意事项

各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~:

① 死锁的概念及其trace文件

② 死锁的分类

③ 行级死锁的模拟

④ ITL的概念、ITL结构

⑤ ITL引发的死锁处理

⑥ ITL死锁的模拟


Tips:

① 本文在itpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和微信公众号(xiaomaimiaolhr)上有同步更新。

② 文章中用到的所有代码、相关软件、相关资料及本文的pdf版本都请前往小麦苗的云盘下载,小麦苗的云盘地址见:http://blog.itpub.net/26736162/viewspace-1624453/

③ 若网页文章代码格式有错乱,请尝试以下办法:①去博客园地址阅读,②下载pdf格式的文档来阅读。

④ 在本篇BLOG中,代码输出部分一般放在一行一列的表格中。其中,需要特别关注的地方我都用灰色背景和粉红色字体来表示,比如在下边的例子中,thread 1的最大归档日志号为33,thread 2的最大归档日志号为43是需要特别关注的地方;而命令一般使用黄色背景和红色字体标注;对代码或代码输出部分的注释一般采用蓝色字体表示。

  List of Archived Logs in backup set 11

  Thrd Seq     Low SCN    Low Time            Next SCN   Next Time

  ---- ------- ---------- ------------------- ---------- ---------

  1    32      1621589    2015-05-29 11:09:52 1625242    2015-05-29 11:15:48

  1    33      1625242    2015-05-29 11:15:48 1625293    2015-05-29 11:15:58

  2    42      1613951    2015-05-29 10:41:18 1625245    2015-05-29 11:15:49

  2    43      1625245    2015-05-29 11:15:49 1625253    2015-05-29 11:15:53

[ZHLHRDB1:root]:/>lsvg -o

T_XLHRD_APP1_vg

rootvg

[ZHLHRDB1:root]:/>

00:27:22 SQL> alter tablespace idxtbs read write;

====2097152*512/1024/1024/1024=1G

本文如有错误或不完善的地方请大家多多指正,ITPUB留言或QQ皆可,您的批评指正是我写作的最大动力。

2.2  本文简介

写了近大半年的书了,碰到了各种困难,不过幸运的是基本上都一一克服了。前段时间工作上碰到了一个很奇怪的死锁问题,由业务发出来的SQL来看是不太可能产生死锁的,不过的的确确实实在在的产生了,那作者是碰到了哪一类的死锁呢?ITL死锁!!有关当时的案例可以参考:http://blog.itpub.net/26736162/viewspace-2124771/http://blog.itpub.net/26736162/viewspace-2124735/。于是,作者就把死锁可能出现的情况都分类总结了一下,分享给大家,欢迎大家指出错误。本文内容也将写入作者的新书中,欢迎大家提前订阅。

第二章 死锁(DeadLock)的分类及其模拟

1  死锁简介

1.1  什么是死锁?

所谓死锁,是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。Oracle对于“死锁”是要做处理的,而不是不闻不问。

[ZFLHRDB1:oracle]:/oracle>oerr ora 60

00060, 00000, "deadlock detected while waiting for resource"

// *Cause:  Transactions deadlocked one another while waiting for resources.

// *Action: Look at the trace file to see the transactions and resources

//          involved. Retry if necessary.


Cause: Your session and another session are waiting for are source locked by the other. This condition is known AS a deadlock. To resolve the deadlock, one or more statements were rolled back for the other session to continue work.

Action Either: l. Enter arollback statement and re—execute all statements since the last commit or 2. Wait until the lock is released, possibly a few minutes, and then re—execute the rolled back statements.

1.2  死锁的trace文件

Oracle中产生死锁的时候会在alert告警日志文件中记录死锁的相关信息,无论单机还是RAC环境都有Deadlock这个关键词,而且当发生死锁时都会生成一个trace文件,这个文件名在alert文件中都有记载。由于在RAC环境中,是由LMD(Lock Manager Daemon)进程统一管理各个节点之间的锁资源的,所以,RAC环境中trace文件是由LMD进程来生成的。

在RAC环境中,告警日志的形式如下所示:

Mon Jun 20 10:10:56 2016

Global Enqueue Services Deadlock detected. More info in file

/u01/app/oracle/diag/rdbms/raclhr/raclhr2/trace/raclhr2_lmd0_19923170.trc.

在单机环境中,告警日志的形式如下所示:

Mon Jun 20 12:10:56 2016

ORA-00060: Deadlock detected. More info in file /u01/app/oracle/diag/rdbms/dlhr/dlhr/trace/dlhr_ora_16973880.trc.

通常来讲,对于单机环境,当有死锁发生后,在trace文件中会看到如下的日志信息:

wpsA551.tmp 

图2-1 单机环境下的死锁

当看到trace文件时,需要确认一下产生锁的类型,是两行还是一行,是TX还是TM,如果只有一行那么说明是同一个SESSION,可能是自治事务引起的死锁。

对于RAC环境,当有死锁发生后,在trace文件中会看到如下的日志信息:

wpsA552.tmp 

图2-2 RAC环境下的死锁

1.3  死锁的检测时间

死锁的监测时间是由隐含参数_lm_dd_interval来控制的,在Oracle 11g中,隐含参数_lm_dd_interval的值默认为10,而在Oracle 10g中该参数默认为60,单位为秒。

SYS@oraLHRDB2> SELECT A.INDX,

  2         A.KSPPINM NAME,

  3         A.KSPPDESC,

  4         B.KSPPSTVL

  5  FROM   X$KSPPI  A,

  6         X$KSPPCV B

  7  WHERE  A.INDX = B.INDX

  8  AND LOWER(A.KSPPINM) LIKE  LOWER('%&PARAMETER%');

ENTER VALUE FOR PARAMETER: _lm_dd_interval

OLD   8: AND LOWER(A.KSPPINM) LIKE  LOWER('%&PARAMETER%')

NEW   8: AND LOWER(A.KSPPINM) LIKE  LOWER('%_LM_DD_INTERVAL%')

 

      INDX NAME               KSPPDESC                       KSPPSTVL

---------- ------------------ ------------------------------ --------------------

       578 _lm_dd_interval    dd time interval in seconds    10

可以看到该隐含参数的值为10。

2  死锁的分类

有人的地方就有江湖,有资源阻塞的地方就可能有死锁。Oralce中最常见的死锁分为:行级死锁(Row-Level Deadlock)和块级死锁(Block-Level Deadlock),其中,行级死锁分为①主键、唯一索引的死锁(会话交叉插入相同的主键值),②外键未加索引,③表上的位图索引遭到并发更新,④常见事务引发的死锁(例如,两个表之间不同顺序相互更新操作引起的死锁;同一张表删除和更新之间引起的死锁),⑤自治事务引发的死锁。块级死锁主要指的是ITL(Interested Transaction List)死锁。

死锁分类图如下所示:

wpsA553.tmp 

图2-3 死锁的分类图


2.1  行级死锁

行级锁的发生如下图所示,在A时间,TRANSACRION1和TRANSCTION2分别锁住了它们要UPDATE的一行数据,没有任何问题。但每个TRANSACTION都没有终止。接下来在B时间,它们又试图UPDATE当前正被对方TRANSACTION锁住的行,因此双方都无法获得资源,此时就出现了死锁。之所以称之为死锁,是因为无论每个TRANSACTION等待多久,这种锁都不会被释放。

wpsA554.tmp 

行级锁的死锁一般是由于应用逻辑设计的问题造成的,其解决方法是通过分析trace文件定位出造成死锁的SQL语句、被互相锁住资源的对象及其记录等信息,提供给应用开发人员进行分析,并修改特定或一系列表的更新(UPDATE)顺序。

以下模拟各种行级死锁的产生过程,版本都是11.2.0.4。

2.1.1  主键、唯一索引的死锁(会话交叉插入相同的主键值)

主键的死锁其本质是唯一索引引起的死锁,这个很容易模拟出来的,新建一张表,设置主键(或创建唯一索引)后插入一个值,然后不要COMMIT,另一个会话插入另一个值,也不要COMMIT,然后再把这两个插入的值互相交换一下,在两个会话中分别插入,死锁就会产生。

会话1,sid为156:

SYS@lhrdb> set sqlprompt "_user'@'_connect_identifier S1> "

SYS@lhrdb S1> DROP TABLE T_DEADLOCK_PRIMARY_LHR;

 

Table dropped.

 

====>>>>> CREATE UNIQUE INDEX IDX_TEST_LHR ON T_DEADLOCK_PRIMARY_LHR(ID);

SYS@lhrdb S1> CREATE TABLE T_DEADLOCK_PRIMARY_LHR(ID NUMBER PRIMARY KEY);

 

Table created.

 

SYS@lhrdb S1> select userenv('sid') from dual;

 

USERENV('SID')

--------------

           156

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(1);

 

1 row created.

 

SYS@lhrdb S1>

会话2,sid为156:

SYS@lhrdb> set sqlprompt "_user'@'_connect_identifier S2> "

SYS@lhrdb S2> select userenv('sid') from dual;

 

USERENV('SID')

--------------

           191

 

SYS@lhrdb S2> INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(2);

 

1 row created.

 

SYS@lhrdb S2> INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(1);

 

 

 

====>>>>> 产生了阻塞


wpsA555.tmp 

SELECT NVL(A.SQL_ID, A.PREV_SQL_ID) SQL_ID,

       A.BLOCKING_SESSION,

       A.SID,

       A.SERIAL#,

       A.LOGON_TIME,

       A.EVENT

  FROM GV$SESSION A

 WHERE A.SID IN (156,191)

 ORDER BY A.LOGON_TIME;

wpsA565.tmp 

156阻塞了191会话,即会话1阻塞了会话2。

会话1再次插入数据:

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(2);

 

 

 

 

====>>>>> 产生了阻塞


此时,去会话2看的时候,已经报出了死锁的错误:

wpsA566.tmp 

此时的阻塞已经发生了变化:

wpsA567.tmp 

告警日志:

Fri Sep 23 09:03:11 2016

ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_25886852.trc.

其内容可以看到很经典的一段:

Deadlock graph:

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

Resource Name          process session holds waits  process session holds waits

TX-0008000c-000008dc        38     191     X             29     156           S

TX-00030016-00000892        29     156     X             38     191           S

 

session 191: DID 0001-0026-00000115     session 156: DID 0001-001D-0000000D

session 156: DID 0001-001D-0000000D     session 191: DID 0001-0026-00000115

 

Rows waited on:

  Session 191: no row

  Session 156: no row

wpsA568.tmp 

这就是主键的死锁,模拟完毕。

此时,若是会话2执行提交后,会话1就会报错,违反唯一约束:

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(2);

 

 

INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(2)

*

ERROR at line 1:

ORA-00001: unique constraint (SYS.SYS_C0011517) violated

一、  脚本

会话1

DROP TABLE T_DEADLOCK_PRIMARY_LHR;

CREATE TABLE T_DEADLOCK_PRIMARY_LHR(ID NUMBER PRIMARY KEY);

--CREATE UNIQUE INDEX IDX_TEST_LHR ON T_DEADLOCK_PRIMARY_LHR(ID);

select userenv('sid') from dual;

INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(1);

 

会话2

select userenv('sid') from dual;

INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(2);

INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(1);

 

 

会话1:---死锁产生

INSERT INTO T_DEADLOCK_PRIMARY_LHR VALUES(2);

 

 

SELECT NVL(A.SQL_ID, A.PREV_SQL_ID) SQL_ID,

       A.BLOCKING_SESSION,

       A.SID,

       A.SERIAL#,

       A.LOGON_TIME,

       A.EVENT

  FROM GV$SESSION A

WHERE A.SID IN (156,191)

ORDER BY A.LOGON_TIME;


2.1.2  外键的死锁(外键未加索引)

外键未加索引很容易导致死锁。在以下两种情况下,Oracle在修改父表后会对子表加一个全表锁:

1. 如果更新了父表的主键,由于外键上没有索引,所以子表会被锁住。

2. 如果删除了父表中的一行,由于外键上没有索引,整个子表也会被锁住。

总之,就是更新或者删除父表的主键,都会导致对其子表加一个全表锁。

如果父表存在删除记录或者更改外键列的情形,那么就需要在子表上为外键列创建索引。

除了全表锁外,在以下情况下,未加索引的外键也可能带来问题:

1. 如果有ON DELETE CASCADE,而且没有对子表加索引:例如,EMP是DEPT的子表,DELETE DEPTNO = 10应该CASCADE(级联)至EMP[4]。如果EMP中的DEPTNO没有索引,那么删除DEPT表中的每一行时都会对EMP做一个全表扫描。这个全表扫描可能是不必要的,而且如果从父表删除多行,父表中每删除一行就要扫描一次子表。

2. 从父表查询子表:再次考虑EMP/DEPT例子。利用DEPTNO查询EMP表是相当常见的。如果频繁地运行以下查询(例如,生成一个报告),你会发现没有索引会使查询速度变慢: 

SELECT * FROM DEPT, EMP 

WHERE EMP.DEPTNO = DEPT.DEPTNO

AND DEPT.DEPTNO = :X; 

那么,什么时候不需要对外键加索引呢?答案是,一般来说,当满足以下条件时不需要加索引: 

     1.  没有从父表删除行。

     2.  没有更新父表的惟一键/主键值。

     3.  没有从父表联结子表。

如果满足上述全部3个条件,那你完全可以跳过索引,不需要对外键加索引。不过个人还是强烈建议对子表添加索引,既然已经创建了外键,就不在乎再多一个索引吧,因为一个索引所增加的代价比如死锁,与缺失这个索引所带来的问题相比,是微不足道的。 

子表上为外键列建立索引,可以:

1)提高针对外键列的查询或改动性能

2)减小表级锁粒度,降低死锁发生的可能性

外键的死锁可以这样通俗的理解:有两个表A和B:A是父表,B是子表。如果没有在B表中的外键加上索引,那么A表在更新或者删除主键时,都会在表B上加一个全表锁。这是为什么呢?因为我们没有给外键加索引,在更新或者删除A表主键的时候,需要查看子表B中是否有对应的记录,以判断是否可以更新删除。那如何查找呢?当然只能在子表B中一条一条找了,因为我们没有加索引吗。既然要在子表B中一条一条地找,那就得把整个子表B都锁定了。由此就会导致以上一系列问题。

实验过程:

会话1首先建立子表和父表

SYS@lhrdb S1> CREATE TABLE T_DEADLOCK_P_LHR (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30)); --父表的主键

 

Table created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_P_LHR VALUES (1, 'A');

 

1 row created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_P_LHR VALUES (2, 'B');

 

1 row created.

 

SYS@lhrdb S1> CREATE TABLE T_DEADLOCK_F_LHR (FID NUMBER, FNAME VARCHAR2(30), FOREIGN KEY (FID) REFERENCES T_DEADLOCK_P_LHR);   --子表的外键

 

Table created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_F_LHR VALUES (1, 'C');

 

1 row created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_F_LHR VALUES (2, 'D');

 

1 row created.

 

SYS@lhrdb S1>

SYS@lhrdb S1> COMMIT;

 

Commit complete.

 


会话1执行一个删除操作,这时候在子表和父表上都加了一个Row-X(SX)锁

SYS@lhrdb S1> delete from T_DEADLOCK_F_LHR where FID=1;

 

1 row deleted.

 

SYS@lhrdb S1> delete from T_DEADLOCK_P_LHR where id=1;

 

1 row deleted.


查询会话1的锁信息:

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 (156,191)

 ORDER BY SID;

wpsA569.tmp 

BLOCK为0表示没有阻塞其它的锁。


会话2:执行另一个删除操作,发现这时候第二个删除语句等待

SYS@lhrdb S2> delete from T_DEADLOCK_F_LHR where FID=2;

 

1 row deleted.

 

SYS@lhrdb S2> delete from T_DEADLOCK_P_LHR where id=2;

 

 

 

 

====>>>>> 产生了阻塞


wpsA56A.tmp 

wpsA56B.tmp 

BLOCK为1表示阻塞了其它的锁。

会话1执行删除语句,死锁发生

SYS@lhrdb S1> delete from T_DEADLOCK_P_LHR where id=1;

 

 

 

====>>>>> 产生了阻塞,而会话2产生了死锁


wpsA56C.tmp 

SYS@lhrdb S2> delete from T_DEADLOCK_P_LHR where id=2;

 

 

 

delete from T_DEADLOCK_P_LHR where id=2

            *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

 

 

 

 


wpsA56D.tmp 

告警日志:

Fri Sep 23 10:31:10 2016

ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_25886852.trc.


查看内容:

*** 2016-09-23 10:31:10.212

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

TM-00017731-00000000        38     191    SX   SSX       29     156    SX   SSX

TM-00017731-00000000        29     156    SX   SSX       38     191    SX   SSX

 

session 191: DID 0001-0026-00000115     session 156: DID 0001-001D-0000000D

session 156: DID 0001-001D-0000000D     session 191: DID 0001-0026-00000115

 

Rows waited on:

  Session 191: no row

  Session 156: no row

wpsA56E.tmp 

回滚会话建立外键列上的索引:

SYS@lhrdb S1> rollback;

 

Rollback complete.

 

SYS@lhrdb S1> create index ind_p_f_fid on T_DEADLOCK_F_LHR(fid);

 

Index created.

重复上面的步骤会话1删除子表记录:

---会话1:

SYS@lhrdb S1> delete from T_DEADLOCK_F_LHR where FID=1;

 

1 row deleted.

 

SYS@lhrdb S1> delete from T_DEADLOCK_P_LHR where id=1;

 

1 row deleted.

 

SYS@lhrdb S1>

 

---会话2:

SYS@lhrdb S2> delete from T_DEADLOCK_F_LHR where FID=2;

 

1 row deleted.

 

SYS@lhrdb S2> delete from T_DEADLOCK_P_LHR where id=2;

 

1 row deleted.

所有的删除操作都可以成功执行,也没有阻塞的生成,重点就是在外键列上建立索引。

wpsA56F.tmp 

wpsA580.tmp 

一、  脚本


--主表

DROP TABLE T_DEADLOCK_P_LHR;

CREATE TABLE T_DEADLOCK_P_LHR (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30)); --父表的主键

INSERT INTO T_DEADLOCK_P_LHR VALUES (1, 'A');

INSERT INTO T_DEADLOCK_P_LHR VALUES (2, 'B');

 

--子表

DROP TABLE T_DEADLOCK_F_LHR;

CREATE TABLE T_DEADLOCK_F_LHR (FID NUMBER, FNAME VARCHAR2(30), FOREIGN KEY (FID) REFERENCES T_DEADLOCK_P_LHR);  --子表的外键

INSERT INTO T_DEADLOCK_F_LHR VALUES (1, 'C');

INSERT INTO T_DEADLOCK_F_LHR VALUES (2, 'D');

 

COMMIT;

 

 

---执行一个删除操作,这时候在子表和父表上都加了一个Row-S(SX)

delete from T_DEADLOCK_F_LHR where FID=1;

delete from T_DEADLOCK_P_LHR where id=1;

---会话2:执行另一个删除操作,发现这时候第二个删除语句等待

delete from T_DEADLOCK_F_LHR where FID=2;

delete from T_DEADLOCK_P_LHR where id=2;

---会话1:死锁发生

delete from T_DEADLOCK_P_LHR where id=1;

 

 

---回滚会话建立外键列上的索引:

create index ind_p_f_fid on T_DEADLOCK_F_LHR(fid);

 

 

--重复上面的步骤会话1删除子表记录: 

---会话1

delete from T_DEADLOCK_F_LHR where FID=1;

delete from T_DEADLOCK_P_LHR where id=1;

---会话2:执行另一个删除操作,发现这时候第二个删除语句等待

delete from T_DEADLOCK_F_LHR where FID=2;

delete from T_DEADLOCK_P_LHR where id=2;


2.1.3  位图(BITMAP)索引死锁

表上的位图索引遭到并发更新也很容易产生死锁。在有位图索引存在的表上面,其实很容易就引发阻塞与死锁。这个阻塞不是发生在表上面,而是发生在索引上。因为位图索引锁定的范围远远比普通的b-tree索引锁定的范围大。

SYS@lhrdb S1> CREATE TABLE T_DEADLOCK_BITMAP_LHR (ID NUMBER , NAME VARCHAR2(10));

 

Table created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (1,'A');

 

1 row created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (1,'B');

 

1 row created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (1,'C');

 

1 row created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (2,'A');

 

1 row created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (2,'B');

 

1 row created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (2,'C');

 

1 row created.

 

SYS@lhrdb S1> COMMIT;

 

Commit complete.

 

--那么在ID列上建bitmap index的话,所有ID=1的会放到一个位图中,所有ID=2的是另外一个位图,而在执行DML操作的时候,锁定的将是整个位图中的所有行,而不仅仅是DML涉及到的行。由于锁定的粒度变粗,bitmap index更容易导致死锁的发生。

会话1:此时所有ID=1的行都被锁定

SYS@lhrdb S1> CREATE BITMAP INDEX IDX_DEADLOCK_BITMAP_LHR ON T_DEADLOCK_BITMAP_LHR(ID);

 

Index created.

 

SYS@lhrdb S1> UPDATE T_DEADLOCK_BITMAP_LHR SET ID=3 WHERE ID=1 AND NAME='A';

 

1 row updated.

 

会话2:此时所有ID=2的行都被锁定

SYS@lhrdb S2> UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='A';

 

1 row updated.

 

会话1:此时会话被阻塞

SYS@lhrdb S1> UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='B';

 

 

 

 

====>>>>> 产生了阻塞


wpsA581.tmp 

会话2:会话被阻塞

SYS@lhrdb S2> UPDATE T_DEADLOCK_BITMAP_LHR SET ID=3 WHERE ID=1 AND NAME='B';

 

 

 

====>>>>> 产生了阻塞

再回到SESSION 1,发现系统检测到了死锁的发生

SYS@lhrdb S1> UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='B';

UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='B'

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

 

告警日志:

Fri Sep 23 11:20:21 2016

ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_19726460.trc.

内容:

*** 2016-09-23 11:26:51.264

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-0009000e-00000b0f        29     156     X             38     191           S

TX-00070001-00000b2c        38     191     X             29     156           S

 

session 156: DID 0001-001D-0000000D     session 191: DID 0001-0026-00000115

session 191: DID 0001-0026-00000115     session 156: DID 0001-001D-0000000D

 

Rows waited on:

  Session 156: obj - rowid = 00017734 - AAAXc0AAAAAAAAAAAA

  (dictionary objn - 96052, file - 0, block - 0, slot - 0)

  Session 191: obj - rowid = 00017734 - AAAXc0AAAAAAAAAAAA

  (dictionary objn - 96052, file - 0, block - 0, slot - 0)


wpsA582.tmp 

死锁发生的根本原因是对于资源的排他锁定顺序不一致。上面的试验中,session1对于bitmap index中的2个位图是先锁定ID=1的位图,然后请求ID=2的位图,而在此之前ID=2的位图已经被session2锁定。session2则先锁定ID=2的位图,然后请求ID=2的位图,而此前ID=1的位图已经被session1锁定。于是,session1等待session2释放ID=2的位图上的锁,session2等待session1释放ID=1的位图上的锁,死锁就发生了

而如果我们创建的是普通的B*Tree index,重复上面的试验则不会出现任何的阻塞和死锁,这是因为锁定的只是DML操作涉及到的行,而不是所有ID相同的行。

一、  脚本

CREATE TABLE T_DEADLOCK_BITMAP_LHR (ID NUMBER , NAME VARCHAR2(10));

INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (1,'A');

INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (1,'B');

INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (1,'C');

INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (2,'A');

INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (2,'B');

INSERT INTO T_DEADLOCK_BITMAP_LHR VALUES (2,'C');

COMMIT;

 

CREATE BITMAP INDEX IDX_DEADLOCK_BITMAP_LHR ON T_DEADLOCK_BITMAP_LHR(ID);

 

--会话1:此时所有ID=1的行都被锁定

UPDATE T_DEADLOCK_BITMAP_LHR SET ID=3 WHERE ID=1 AND NAME='A';

--会话2:此时所有ID=2的行都被锁定

UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='A';

 

--会话1:此时会话被阻塞

UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='B';

 

--会话2:会话被阻塞,再回到SESSION 1,发现系统检测到了死锁的发生

UPDATE T_DEADLOCK_BITMAP_LHR SET ID=3 WHERE ID=1 AND NAME='B';

 

--会话1

UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='B';

UPDATE T_DEADLOCK_BITMAP_LHR SET ID=4 WHERE ID=2 AND NAME='B'

*

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource


2.1.4  常见事务引发的死锁

如果你有两个会话,每个会话都持有另一个会话想要的资源,此时就会出现死锁(deadlock)。例如,如果我的数据库中有两个表A和B,每个表中都只有一行,就可以很容易地展示什么是死锁。我要做的只是打开两个会话(例如,两个SQL*Plus会话)。在会话A中更新表A,并在会话B中更新表B。现在,如果我想在会话B中更新表A,就会阻塞。会话A已经锁定了这一行。这不是死锁;只是阻塞而已。因为会话A还有机会提交或回滚,这样会话B就能继续。如果我再回到会话A,试图更新表B,这就会导致一个死锁。要在这两个会话中选择一个作为“牺牲品”,让它的语句回滚。 

想要更新表B的会话A还阻塞着,Oracle不会回滚整个事务。只会回滚与死锁有关的某条语句。会话B仍然锁定着表B中的行,而会话A还在耐心地等待这一行可用。收到死锁消息后,会话B必须决定将表B上未执行的工作提交还是回滚,或者继续走另一条路,以后再提交。一旦这个会话执行提交或回滚,另一个阻塞的会话就会继续,好像什么也没有发生过一样。

一、  模拟一:两个表之间不同顺序相互更新操作引起的死锁

1、创建两个简单的表A和B,每个表中仅仅包含一个字段id。

[ZFZHLHRDB2:oracle]:/oracle>ORACLE_SID=raclhr2

[ZFZHLHRDB2:oracle]:/oracle>sqlplus / as sysdba

 

SQL*Plus: Release 11.2.0.4.0 Production on Mon Jun 20 09:40:24 2016

 

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

 

 

Connected to:

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,

Data Mining and Real Application Testing options

 

SYS@raclhr2> select * from v$version;

 

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

PL/SQL Release 11.2.0.4.0 - Production

CORE    11.2.0.4.0      Production

TNS for IBM/AIX RISC System/6000: Version 11.2.0.4.0 - Production

NLSRTL Version 11.2.0.4.0 - Production

 

SYS@raclhr2> show parameter cluster

 

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

cluster_database                     boolean     TRUE

cluster_database_instances           integer     2

cluster_interconnects                string

 

SYS@raclhr2> create table A (id int);

 

Table created.

 

SYS@raclhr2> create table B (id int);

 

Table created.

 

2、每张表中仅初始化一条数据

SYS@raclhr2> insert into A values (1);

 

1 row created.

 

SYS@raclhr2> insert into B values (2);

 

1 row created.

 

SYS@raclhr2> commit;

 

Commit complete.


3、在第一个会话session1中更新表A中的记录“1”为“10000”,不提交在第二个会话session2中更新表B中的记录“2”为“20000”,不提交

session1的情况:

SYS@raclhr2> insert into A values (1);

 

1 row created.

 

SYS@raclhr2> insert into B values (2);

 

1 row created.

 

SYS@raclhr2> commit;

 

Commit complete.


SYS@raclhr2> SELECT a.SID,

  2         b.SERIAL# ,

  3         c.SPID

  4  FROM   v$mystat  a,

  5         v$session b ,

  6         v$process c

  7  WHERE  a.SID = b.SID

  8  and b.PADDR=c.ADDR

  9  AND    rownum = 1;

 

       SID    SERIAL# SPID

---------- ---------- ------------------------

       133          3 20906088

 

SYS@raclhr2>  update A set id = 10000 where id = 1;

 

1 row updated.

 

session2的情况:

SYS@raclhr2> SELECT a.SID,

  2         b.SERIAL# ,

  3         c.SPID

  4  FROM   v$mystat  a,

  5         v$session b ,

  6         v$process c

  7  WHERE  a.SID = b.SID

  8  and b.PADDR=c.ADDR

  9  AND    rownum = 1;

 

       SID    SERIAL# SPID

---------- ---------- ------------------------

       195         21 11010172

 

SYS@raclhr2> update B set id = 20000 where id = 2;

 

1 row updated.

 

SYS@raclhr2>


4、此时,没有任何问题发生。OK,现在注意一下下面的现象,我们再回到会话session1中,更新表B的记录,此时出现了会话阻塞,更新hang住不能继续。

SYS@raclhr2> update B set id = 10000 where id = 2;


这里出现了“锁等待”(“阻塞”)的现象,原因很简单,因为在session2中已经对这条数据执行过update操作没有提交表示已经对该行加了行级锁。

SYS@raclhr2> set line 9999

SYS@raclhr2> SELECT A.INST_ID,

  2         A.SID,

  3         A.SERIAL#,

  4         A.SQL_ID,

  5         A.BLOCKING_INSTANCE,

  6         A.BLOCKING_SESSION,

  7         A.EVENT

  8    FROM gv$session a

  9   WHERE a.USERNAME IS NOT NULL

10     and a.STATUS = 'ACTIVE'

11     and a.BLOCKING_SESSION IS NOT NULL ;

 

   INST_ID        SID    SERIAL# SQL_ID        BLOCKING_INSTANCE BLOCKING_SESSION EVENT

---------- ---------- ---------- ------------- ----------------- ---------------- ------------------------------

         2        133          3 6k793mj0duubw                 2              195 enq: TX - row lock contention

我们可以通过v$session视图看到,实例2的195阻塞了实例2的133会话,即本实验中的session2阻塞了session1。

6、接下来再执行一条SQL后死锁就会产生了:在session2中,更新表A的记录

SYS@raclhr2> update A set id = 10000 where id = 1;

这里还是长时间的等待,但是这里发生了死锁,这个时候我们去第一个会话session1中看一下,原先一直在等待的SQL语句报了如下的错误:

SYS@raclhr2> update B set id = 10000 where id = 2;

update B set id = 10000 where id = 2

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

 

若此时查询v$session视图可以看到实例2的133阻塞了实例2的195会话,即本实验中的session1阻塞了session2,和刚刚的阻塞情况相反,说明oracle做了自动处理:

SYS@raclhr2> set line 9999

SYS@raclhr2> SELECT A.INST_ID,

  2         A.SID,

  3         A.SERIAL#,

  4         A.SQL_ID,

  5         A.BLOCKING_INSTANCE,

  6         A.BLOCKING_SESSION,

  7         A.EVENT

  8    FROM gv$session a

  9   WHERE a.USERNAME IS NOT NULL

10     and a.STATUS = 'ACTIVE'

11      and a.BLOCKING_SESSION IS NOT NULL ;

 

   INST_ID        SID    SERIAL# SQL_ID        BLOCKING_INSTANCE BLOCKING_SESSION EVENT

---------- ---------- ---------- ------------- ----------------- ---------------- ------------------------------

         2        195         21 5q7t3877fdu3n                 2              133 enq: TX - row lock contention

 

更进一步:查看一下alert警告日志文件发现有如下的记录:

Mon Jun 20 10:10:56 2016

Global Enqueue Services Deadlock detected. More info in file

/u01/app/oracle/diag/rdbms/raclhr/raclhr2/trace/raclhr2_lmd0_19923170.trc.

若是单机环境,报警日志为:

ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/diag/rdbms/dlhr/dlhr/trace/dlhr_ora_16973880.trc.

wpsA583.tmp wpsA584.tmp

可以看到该文件是由lmd进程生成的,为rac的特有进程,完成CacheFusion的作用,再进一步:看看系统自动生成的trace文件中记录了什么:

user session for deadlock lock 0x70001001569c378

  sid: 133 ser: 3 audsid: 4294967295 user: 0/SYS

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

    flags2: (0x40009) -/-/INC

  pid: 60 O/S info: user: oracle, term: UNKNOWN, ospid: 20906088

    image: oracle@ZFZHLHRDB2 (TNS V1-V3)

  client details:

    O/S info: user: oracle, term: pts/0, ospid: 16122014

    machine: ZFZHLHRDB2 program: sqlplus@ZFZHLHRDB2 (TNS V1-V3)

    application name: sqlplus@ZFZHLHRDB2 (TNS V1-V3), hash value=346557658

  current SQL:

  update B set id = 10000 where id = 2

DUMP LOCAL BLOCKER: initiate state dump for DEADLOCK

  possible owner[60.20906088] on resource TX-00140013-0000072D

 

 

user session for deadlock lock 0x700010015138660

  sid: 195 ser: 21 audsid: 4294967295 user: 0/SYS

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

    flags2: (0x40009) -/-/INC

  pid: 46 O/S info: user: oracle, term: UNKNOWN, ospid: 11010172

    image: oracle@ZFZHLHRDB2 (TNS V1-V3)

  client details:

    O/S info: user: oracle, term: pts/1, ospid: 16646154

    machine: ZFZHLHRDB2 program: sqlplus@ZFZHLHRDB2 (TNS V1-V3)

    application name: sqlplus@ZFZHLHRDB2 (TNS V1-V3), hash value=346557658

  current SQL:

  update A set id = 10000 where id = 1

DUMP LOCAL BLOCKER: initiate state dump for DEADLOCK

  possible owner[46.11010172] on resource TX-000B0018-00000416

 

若是单机环境比较明显:

[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-000d0005-00000047        41      37     X             25      34           X

TX-0008000a-0000036b        25      34     X             41      37           X

 

session 37: DID 0001-0029-00000003session 34: DID 0001-0019-0000000D

session 34: DID 0001-0019-0000000Dsession 37: DID 0001-0029-00000003

 

Rows waited on:

  Session 37: obj - rowid = 00015FE7 - AAAV/nAABAAAXeBAAA

  (dictionary objn - 90087, file - 1, block - 96129, slot - 0)

  Session 34: obj - rowid = 00015FE6 - AAAV/mAABAAAXZ5AAA

  (dictionary objn - 90086, file - 1, block - 95865, slot - 0)


注意trace文件中的一行如下提示信息,说明一般情况下都是应用和人为的,和Oracle同学没有关系:

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.


二、  模拟二:同一张表删除和更新之间引起的死锁

造成死锁的原因就是多个线程或进程对同一个资源的争抢或相互依赖。这里列举一个对同一个资源的争抢造成死锁的实例。

SYS@lhrdb S1> CREATE  TABLE T_DEADLOCK_DU_LHR (    ID  NUMBER,  test  VARCHAR(10) )  ;

 

Table created.

 

SYS@lhrdb S1> INSERT  INTO  T_DEADLOCK_DU_LHR  VALUES(1,'test1'); 

 

1 row created.

 

SYS@lhrdb S1> INSERT  INTO  T_DEADLOCK_DU_LHR  VALUES(2,'test2'); 

 

1 row created.

 

SYS@lhrdb S1> COMMIT; 

 

Commit complete.

 

SYS@lhrdb S1> SELECT * FROM T_DEADLOCK_DU_LHR;

 

        ID TEST

---------- ----------

         1 test1

         2 test2

会话1更新第一条记录:

SYS@lhrdb S1> UPDATE T_DEADLOCK_DU_LHR T SET T.TEST=100 WHERE ID=1;

 

1 row updated.

 

会话2删除第二条记录:

SYS@lhrdb S2> DELETE FROM  T_DEADLOCK_DU_LHR T  WHERE ID=2;

 

1 row deleted.

 

接下来会话1更新第二条记录,这是就产生了阻塞:

SYS@lhrdb S1> UPDATE T_DEADLOCK_DU_LHR T SET T.TEST=100 WHERE ID=2;

 

 

 

====>>>>> 产生了阻塞,会话2阻塞了会话1

 

wpsA585.tmp 

会话2删除第一条记录:

SYS@lhrdb S2> DELETE FROM  T_DEADLOCK_DU_LHR T  WHERE ID=1;

 

 

 

====>>>>> 产生了阻塞,此时会话1产生死锁

查看会话1:

SYS@lhrdb S1> UPDATE T_DEADLOCK_DU_LHR T SET T.TEST=100 WHERE ID=2;

 

 

 

 

 

UPDATE T_DEADLOCK_DU_LHR T SET T.TEST=100 WHERE ID=2

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

 

 

SYS@lhrdb S1> SYS@lhrdb S1> SYS@lhrdb S1> SYS@lhrdb S1> SYS@lhrdb S1> SYS@lhrdb S1>


wpsA596.tmp 

告警日志:

Fri Sep 23 15:10:55 2016

ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_19726460.trc.

内容:

*** 2016-09-23 15:10:55.326

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-00090008-00000b0c        29     156     X             38     191           X

TX-000a0007-00000d28        38     191     X             29     156           X

 

session 156: DID 0001-001D-0000000D     session 191: DID 0001-0026-00000115

session 191: DID 0001-0026-00000115     session 156: DID 0001-001D-0000000D

 

Rows waited on:

  Session 156: obj - rowid = 0001773F - AAAXc/AABAABc2RAAB

  (dictionary objn - 96063, file - 1, block - 380305, slot - 1)

  Session 191: obj - rowid = 0001773F - AAAXc/AABAABc2RAAA

  (dictionary objn - 96063, file - 1, block - 380305, slot - 0)


wpsA597.tmp 

2.1.5  自治事务引发的死锁

一般来说构成死锁至少需要两个会话,而自治事务是一个会话可能引发死锁。

自治事务死锁情景:存储过程INSERT表A,然后INSERT表B;其中INSERT表A触发TRIGGER T,T也INSERT表B,T是自治事务(AT),AT试图获取对B的锁,结果B已经被主事务所HOLD,这里会报出来ORA-00060 – 等待资源时检查到死锁.

解决方法:去掉了T中的PRAGMA AUTONOMOUS_TRANSACTION声明,保持和存储过程事务一致.

一、  模拟一:更新

在主事务中如果更新了部分记录,这时若自治事务更新同样的记录,就会造成死锁,下面通过一个简单的例子模拟了这个错误的产生:

SYS@lhrdb S1> CREATE TABLE T_DEADLOCK_AT2_LHR(ID NUMBER, NAME VARCHAR2(30));

 

Table created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_AT2_LHR SELECT ROWNUM, TNAME FROM TAB WHERE ROWNUM<=4;

 

4 rows created.

 

SYS@lhrdb S1> COMMIT;

 

Commit complete.

 

SYS@lhrdb S1> CREATE OR REPLACE PROCEDURE PRO_TESTAT_LHR AS

  2    PRAGMA AUTONOMOUS_TRANSACTION;

  3  BEGIN

  4    UPDATE T_DEADLOCK_AT2_LHR SET NAME = NAME WHERE ID = 1;

  5    COMMIT;

  6  END;

  7  /

 

Procedure created.

 

 

SYS@lhrdb S1> UPDATE T_DEADLOCK_AT2_LHR SET NAME = NAME WHERE ID = 1;

 

1 row updated.

 

SYS@lhrdb S1> EXEC PRO_TESTAT_LHR;

 

BEGIN PRO_TESTAT_LHR; END;

 

*

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

ORA-06512: at "SYS.PRO_TESTAT_LHR", line 4

ORA-06512: at line 1

 

 

SYS@lhrdb S1> SYS@lhrdb S1>

在使用自治事务的时候要避免当前事务锁定的记录和自治事务中锁定的记录相互冲突。

告警日志:

Fri Sep 23 14:03:10 2016

ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_19726460.trc.

内容:

*** 2016-09-23 14:10:34.974

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-000a0015-00000d25        38     191     X             38     191           X

 

session 191: DID 0001-0026-00000115     session 191: DID 0001-0026-00000115

 

Rows waited on:

  Session 191: obj - rowid = 0001773A - AAAXc6AABAABc2BAAA

  (dictionary objn - 96058, file - 1, block - 380289, slot - 0)

 

----- Information for the OTHER waiting sessions -----

----- End of information for the OTHER waiting sessions -----

 

Information for THIS session:

 

----- Current SQL Statement for this session (sql_id=3w3thujdh1y3a) -----

UPDATE T_DEADLOCK_AT2_LHR SET NAME = NAME WHERE ID = 1

----- PL/SQL Stack -----

----- PL/SQL Call Stack -----

  object      line  object

  handle    number  name

700010052c9cf40         4  procedure SYS.PRO_TESTAT_LHR

700010052cb7588         1  anonymous block


wpsA598.tmp 

1、  脚本

CREATE TABLE T_DEADLOCK_AT2_LHR(ID NUMBER, NAME VARCHAR2(30));

INSERT INTO T_DEADLOCK_AT2_LHR SELECT ROWNUM, TNAME FROM TAB WHERE ROWNUM<=4;

COMMIT;

 

CREATE OR REPLACE PROCEDURE PRO_TESTAT_LHR AS

  PRAGMA AUTONOMOUS_TRANSACTION;

BEGIN

  UPDATE T_DEADLOCK_AT2_LHR SET NAME = NAME WHERE ID = 1;

  COMMIT;

END;

/

 

UPDATE T_DEADLOCK_AT2_LHR SET NAME = NAME WHERE ID = 1;

exec PRO_TESTAT_LHR;

 

 

 

ERROR AT LINE 1:

ORA-00060: DEADLOCK DETECTED WHILE WAITING FOR RESOURCE

ORA-06512: AT "SYS.PRO_TESTAT_LHR", LINE 4

ORA-06512: AT LINE 3


二、  模拟二:插入

主事务和自治事务插入的是同一个主键值也会引起死锁。

SYS@lhrdb S1> CREATE TABLE T_DEADLOCK_AT_LHR(X INT  PRIMARY KEY,Y INT);

 

Table created.

 

SYS@lhrdb S1> CREATE OR REPLACE PROCEDURE PRO_AUTO_PROC_LHR(P_X INT,P_Y INT) AS

  2  PRAGMA AUTONOMOUS_TRANSACTION;

  3  BEGIN

  4    INSERT INTO T_DEADLOCK_AT_LHR(X,Y) VALUES(P_X,P_Y);

  5    COMMIT;

  6  END;

  7  /

 

Procedure created.

 

SYS@lhrdb S1> INSERT INTO T_DEADLOCK_AT_LHR(X,Y) VALUES(1,1);

 

1 row created.

 

SYS@lhrdb S1> EXEC PRO_AUTO_PROC_LHR(1,2);

BEGIN PRO_AUTO_PROC_LHR(1,2); END;

 

*

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

ORA-06512: at "SYS.PRO_AUTO_PROC_LHR", line 4

ORA-06512: at line 1

告警日志:

Fri Sep 23 13:49:06 2016

ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/diag/rdbms/lhrdb/lhrdb/trace/lhrdb_ora_19726460.trc.

内容:

*** 2016-09-23 13:49:06.546

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-000a000b-00000d28        29     156     X             29     156           S

 

session 156: DID 0001-001D-0000000D     session 156: DID 0001-001D-0000000D

 

Rows waited on:

  Session 156: no row

 

----- Information for the OTHER waiting sessions -----

----- End of information for the OTHER waiting sessions -----

 

Information for THIS session:

 

----- Current SQL Statement for this session (sql_id=4fv0tmjrzv28u) -----

INSERT INTO T_DEADLOCK_AT_LHR(X,Y) VALUES(:B2 ,:B1 )

----- PL/SQL Stack -----

----- PL/SQL Call Stack -----

  object      line  object

  handle    number  name

70001005a9a0580         4  procedure SYS.PRO_AUTO_PROC_LHR

700010052d18b90         1  anonymous block


wpsA599.tmp 

可以看到,等待的和持有锁的是同一个会话,根据trace信息记录的对象,发现问题是自治事务导致的。

1、  脚本

CREATE TABLE T_DEADLOCK_AT_LHR(X INT  PRIMARY KEY,Y INT);

 

 

CREATE OR REPLACE PROCEDURE PRO_AUTO_PROC_LHR(P_X INT,P_Y INT) AS

PRAGMA AUTONOMOUS_TRANSACTION;

BEGIN

  INSERT INTO T_DEADLOCK_AT_LHR(X,Y) VALUES(P_X,P_Y);

  COMMIT;

END;

/

 

 

INSERT INTO T_DEADLOCK_AT_LHR(X,Y) VALUES(1,1);

 

 

 

SQL> EXEC PRO_AUTO_PROC_LHR(1,2);

BEGIN AUTO_PROC(1,2); END;

 

*

ERROR AT LINE 1:

ORA-00060: DEADLOCK DETECTED WHILE WAITING FOR RESOURCE

ORA-06512: AT "BOCNET.AUTO_PROC", LINE 4

ORA-06512: AT LINE 1


2.2  块级死锁

块级死锁其实指的就是ITL死锁。

2.2.1  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结构

如果DUMP一个块(命令:alter system dump datafile X block XXX;),那么在DUMP文件中就可以看到ITL信息:

Itl            Xid                       Uba         Flag   Lck        Scn/Fsc

0x01   0x0008.002.000009e9  0x00c0108b.04ac.24  --U-    3  fsc 0x0000.00752951

0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

wpsA59A.tmp 

1) Itl: ITL事务槽编号,ITL事务槽号的流水编号

2) Xid:代表对应的事务id(transac[X]tion identified),在回滚段事务表中有一条记录和这个事务对应。Xid由三列使用十六进制编码的数字列表示,分别是:Undo Segment Number +Transaction Table Slot Number+ Wrap,即由undo段号+undo槽号+undo槽号的覆盖次数三部分组成,即usn.slot.sqn,这里0x0008.002.000009e9转换为10进制为8.2.2537,从下边的查询出的结果是相对应的。

SYS@lhrdb> select xidusn,xidslot,xidsqn,ubafil,ubablk,ubasqn,ubarec from v$transaction;

    XIDUSN    XIDSLOT     XIDSQN     UBAFIL     UBABLK     UBASQN     UBAREC

---------- ---------- ---------- ---------- ---------- ---------- ----------

         8          2       2537         3        4235       1196          36

3) Uba:(Undo Block Address),该事务对应的回滚段地址,记录了最近一次的该记录的前镜像(修改前的值)。Uba组成:Undo块地址(undo文件号和数据块号)+回滚序列号+回滚记录号。多版本一致读是Oracle保证读操作不会被事务阻塞的重要特性。当Server Process需要查询一个正在被事务修改,但是尚未提交的数据时,就根据ITL上的uba定位到对应Undo前镜像数据位置。这里的Uba为:0x00c0108b.04ac.24,其中00c0108b(16进制)=0000 0000 1100 0000 0001 0000 1000 1011(2进制,共32位,前10位代表文件号,后22位代表数据块号)=文件号为3,块号为4235:(10进制);04ac(16进制)=1196(10进制);24(16进制)=36(10进制)。这个结果和v$transaction查询出来的结果一致。

SELECT UBAFIL 回滚段文件号,UBABLK 数据块号,UBASQN 回滚序列号,UBAREC 回滚记录号 FROM v$transaction ;  --查看UBA

4) Flag:事务标志位,即当前事务槽的状态信息。这个标志位就记录了这个事务的操作状态,各个标志的含义分别是: 

标识

简介

----

事务是活动的,未提交,或者在块清除前提交事务

C---

transaction has been committed and locks cleaned out   --事务已经提交,锁已经被清除(提交)

-B--

this undo record contains the undo for this ITL entry

--U-

transaction committed (maybe long ago); SCN is an upper bound  --事务已经提交,但是锁还没有清除(快速提交)

---T

transaction was still active at block cleanout SCN   --块清除的SCN被记录时,该事务仍然是活动的,块上如果有已经提交的事务,那么在clean ount的时候,块会被进行清除,但是这个块里面的事务不会被清除。

C-U-

块被延迟清除,回滚段的信息已经改写,SCN显示为最小的SCN,需要由由回滚段重新生成,例如在提交以前,数据块已经flush到数据文件上。

5) Lck:表示这个事务所影响的行数,锁住了几行数据,对应有几个行锁。我们看到01号事物槽Lck为3,因为该事物槽中的事物Flag为U,证明该事物已经提交,但是锁还没有清除。再比如对于下边这个ITL:

Itl            Xid                       Uba         Flag    Lck        Scn/Fsc

0x01   0x0006.020.00000271  0x00800205.0257.13  C---     0         scn 0x0000.001732c4

0x02   0x0008.006.00000279  0x00800351.0278.15  ----     1         fsc 0x0000.00000000

我们看到01号事物槽Lck为0,因为该事物槽中的事物Flag为C,证明该事物已经提交,锁也被清楚掉了,该事物槽可以被重用了。02号事物槽Lck为1,是因为我对第一行做了一个更新,并且没有提交,Flag为“----”说明该事物是活动的。

6) Scn/Fsc:Commit SCN或者快速提交(Fast Commit Fsc)的SCN。 Scn=SCN of commited TX; Fsc=Free space credit(bytes)每条记录中的行级锁对应Itl条目lb,对应于Itl列表中的序号,即那个事务在该记录上产生的锁。一个事物只有在提交之后才会在ITL事物槽中记录SCN。

二、  ITL个数

ITL的个数,受参数INITRANS控制,最大ITL个数受MAXTRANS控制(11g已废弃MAXTRANS),在一个块内部,默认分配了2个ITL的个数。ITL是block级的概念,一个ITL占用块46B的空间,参数INITRANS意味着块中除去block header外一部分存储空间无法被记录使用(46B*INITRANS),当块中还有一定的FREE SPACE时,ORACLE可以使用FREE SPACE构建ITL供事务使用,如果这个块内还有空闲空间,那么Oracle是可以利用这些空闲空间并再分配ITL。如果没有了空闲空间(free space),那么,这个块因为不能分配新的ITL,所以就可能发生ITL等待,即enq: TX - allocate ITL entry等待事件。注意:10g以后MAXTRANS参数被废弃,默认最大支持255个并发。

如果在并发量特别大的系统中,最好分配足够的ITL个数,其实它并浪费不了太多的空间,或者,设置足够的PCTFREE,保证ITL能扩展,但是PCTFREE有可能是被行数据给消耗掉的,如UPDATE,所以,也有可能导致块内部的空间不够而导致ITL等待。

对于表(数据块)来说,INITRANS这个参数的默认值是1。对于索引(索引块)来说,这个参数默认值是2。

ITL等待表现出的等待事件为“TX - allocate ITL entry”,根据MOS(Troubleshooting waits for 'enq: TX - allocate ITL entry' (Doc ID 1472175.1)提供的解决办法,需要修改一些参数,SQL如下,这里假设用户名为TLHR,表名为TLHRBOKBAL,表上的索引名为PK_TLHRBOKBAL:

ALTER TABLE TLHR.TLHRBOKBAL PCTFREE 20  INITRANS 16;

ALTER TABLE TLHR.TLHRBOKBAL MOVE NOLOGGING PARALLEL 12;

ALTER TABLE TLHR.TLHRBOKBAL LOGGING NOPARALLEL;

ALTER INDEX TLHR.PK_TLHRBOKBAL  REBUILD PCTFREE 20 INITRANS 16 NOLOGGING PARALLEL 12;

ALTER INDEX TLHR.PK_TLHRBOKBAL LOGGING NOPARALLEL;

无MOS权限的朋友可以去http://blog.itpub.net/26736162/viewspace-2124531/阅读。

2.2.2  ITL引起的死锁案例处理

由ITL不足引发的块级死锁的一个处理案例可以参考我的BLOG:http://blog.itpub.net/26736162/viewspace-2124771/http://blog.itpub.net/26736162/viewspace-2124735/

2.2.3  ITL死锁的模拟

我们首先创建一张表T_ITL_LHR,这里指定PCTFREE为0,INITRANS为1,就是为了观察到ITL的真实等待情况,然后我们给这些块内插入数据,把块填满,让它不能有空间分配。

SYS@lhrdb21> SELECT * FROM V$VERSION;

 

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

PL/SQL Release 11.2.0.4.0 - Production

CORE    11.2.0.4.0      Production

TNS for IBM/AIX RISC System/6000: Version 11.2.0.4.0 - Production

NLSRTL Version 11.2.0.4.0 - Production

 

SYS@lhrdb21> SHOW PARAMETER CLUSTER

 

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

cluster_database                     boolean     TRUE

cluster_database_instances           integer     2

cluster_interconnects                string

 

SYS@lhrdb21> CREATE TABLE T_ITL_LHR(A INT) PCTFREE 0 INITRANS 1;

 

Table created.

 

SYS@lhrdb21> BEGIN

  2    FOR I IN 1 .. 2000 LOOP

  3      INSERT INTO T_ITL_LHR VALUES (I);

  4    END LOOP;

  5  END;

  6  /

 

PL/SQL procedure successfully completed.

 

SYS@lhrdb21> COMMIT;

 

Commit complete.

我们检查数据填充的情况:

SYS@lhrdb21> SELECT F, B, COUNT(*)

  2    FROM (SELECT DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) F,

  3                 DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) B

  4            FROM T_ITL_LHR)

  5   GROUP BY F, B

  6   ORDER BY F,B;

 

         F          B      COUNT(*)

---------- ---------- ----------

         1      94953        734

         1      94954        734

         1      94955        532

可以发现,这2000条数据分布在3个块内部,其中有2个块(94953和94954)填满了,一个块(94955)是半满的。因为有2个ITL槽位,我们需要拿2个满的数据块,4个进程来模拟ITL死锁:

实验步骤

会话

SID

要更新的块号

要更新的行号

是否有阻塞

步骤一

1

19

94953

94953

1

N

2

79

2

N

3

78

94954

94954

1

N

4

139

2

N

会话1:

SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;

 

USERENV('SID')

--------------

            19

 

SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A

  2   WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953 

  3  AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=1;

 

1 row updated.

会话2:

SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;

 

USERENV('SID')

--------------

            79

 

SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A

  2   WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953

  3  AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=2;

 

1 row updated.

 

会话3:

SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;

 

USERENV('SID')

--------------

            78

 

SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A

  2   WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954

  3  AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=1;

 

1 row updated.

 

会话4:

SYS@lhrdb21> SELECT USERENV('SID') FROM DUAL;

 

USERENV('SID')

--------------

           139

 

SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A

  2   WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954

  3  AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=2;

 

1 row updated.

 

 

这个时候系统不存在阻塞,

SELECT NVL(A.SQL_ID, A.PREV_SQL_ID) SQL_ID,

       A.BLOCKING_SESSION,

       A.SID,

       A.SERIAL#,

       A.LOGON_TIME,

       A.EVENT

  FROM GV$SESSION A

 WHERE A.SID IN (19, 79,78,139)

 ORDER BY A.LOGON_TIME;

wpsA5AA.tmp 

以上4个进程把2个不同块的4个ITL槽位给消耗光了,现在的情况,就是让他们互相锁住,达成死锁条件,回到会话1,更新块94954,注意,以上4个操作,包括以下的操作,更新的根本不是同一行数据,主要是为了防止出现的是行锁等待。

实验步骤

会话

SID

要更新的块号

要更新的行号

是否有阻塞

步骤一

1

19

94953

94953

1

N

2

79

2

N

3

78

94954

94954

1

N

4

139

2

N

步骤二

1

19

94954

3

Y

3

78

94953

3

Y


会话1:

UPDATE T_ITL_LHR SET A=A

WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954

AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=3;

wpsA5AB.tmp 

会话1出现了等待。

会话3:

UPDATE T_ITL_LHR SET A=A

WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953

AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=3;

wpsA5AC.tmp 

会话3发现出现了等待。

我们查询阻塞的具体情况:

SELECT NVL(A.SQL_ID, A.PREV_SQL_ID) SQL_ID,

       A.BLOCKING_SESSION,

       A.SID,

       A.SERIAL#,

       A.LOGON_TIME,

       A.EVENT

  FROM GV$SESSION A

 WHERE A.SID IN (19, 79,78,139)

 ORDER BY A.LOGON_TIME;

wpsA5AD.tmp 

可以看到,会话1被会话4阻塞了,会话3被会话2阻塞了。

注意,如果是9i,在这里就报死锁了,但是在10g里面,这个时候,死锁是不会发生的,因为这里的会话1还可以等待会话4释放资源,会话3还可以等待会话2释放资源,只要会话2与会话4释放了资源,整个环境又活了,那么我们需要把这两个进程也塞住。

出现的是行锁等待。

实验步骤

会话

SID

要更新的块号

要更新的行号

是否有阻塞

步骤一

1

19

94953

94953

1

N

2

79

2

N

3

78

94954

94954

1

N

4

139

2

N

步骤二

1

19

94954

3

Y

3

78

94953

3

Y

步骤三

2

79

94954

4

Y

4

139

94953

4

Y


会话2,注意,我们也不是更新的同一行数据:

UPDATE T_ITL_LHR SET A=A

WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954

AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=4;

wpsA5AE.tmp 

会话2出现了等待,具体阻塞情况:

wpsA5AF.tmp 

我做了几次实验,会话2执行完SQL后,会话3到这里就报出了死锁,但有的时候并没有产生死锁,应该跟系统的阻塞顺序有关,若没有产生死锁,我们可以继续会话4的操作。

SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A

  2   WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953

  3  AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=3;

 

 

UPDATE T_ITL_LHR SET A=A

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

会话4,注意,我们也不是更新的同一行数据:

UPDATE T_ITL_LHR SET A=A

WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94953

AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=4;

wpsA5B0.tmp 

会话4发现出现了等待。

wpsA5B1.tmp 

虽然,以上的每个更新语句,更新的都不是同一个数据行,但是,的确,所有的进程都被阻塞住了,那么,死锁的条件也达到了,等待一会(这个时间有个隐含参数来控制的:_lm_dd_interval),我们可以看到,会话2出现提示,死锁:

SYS@lhrdb21> UPDATE T_ITL_LHR SET A=A

  2   WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)=94954

  3  AND DBMS_ROWID.ROWID_ROW_NUMBER(ROWID)=4;

 

UPDATE T_ITL_LHR SET A=A

       *

ERROR at line 1:

ORA-00060: deadlock detected while waiting for resource

报出死锁之后的阻塞情况:

wpsA5B2.tmp 

我们可以在会话2上继续执行步骤三中的SQL,依然会产生死锁。生成死锁后,在告警日志中有下边的语句:

Fri Sep 09 17:56:55 2016

Global Enqueue Services Deadlock detected. More info in file

/oracle/app/oracle/diag/rdbms/lhrdb2/lhrdb21/trace/lhrdb21_lmd0_17039368.trc.

其中的内容有非常经典的一段Global Wait-For-Graph(WFG):

*** 2016-09-09 17:48:22.216

Submitting asynchronized dump request [1c]. summary=[ges process stack dump (kjdglblkrdm1)].

Global blockers dump end:-----------------------------------

Global Wait-For-Graph(WFG) at ddTS[0.395] :

BLOCKED 0x700010063d59b90 3 wq 2 cvtops x1001 TX 0x7000b.0xa67(ext 0x2,0x0)[1002-0029-00008387] inst 1

BLOCKER 0x700010063c6d268 3 wq 1 cvtops x28 TX 0x7000b.0xa67(ext 0x2,0x0)[1002-002D-00003742] inst 1

BLOCKED 0x700010063d5adc8 3 wq 2 cvtops x1 TX 0x30021.0x848(ext 0x2,0x0)[1002-002D-00003742] inst 1

BLOCKER 0x700010063d5a4b8 3 wq 1 cvtops x28 TX 0x30021.0x848(ext 0x2,0x0)[1002-0029-00008387] inst 1

该实验过程可能有点复杂,小麦苗画了个图来说明整个实验过程:

wpsA5B3.tmp




oracle ora-60 deadlock发生在多个会话的情况



 ora-60错误可以发生在两个会话之间,即两个会话之间互相持有一个对方等待的资源(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). )。如果几个会话之间的资源争用是环形,这也是死锁,并且oracle同样会监测到并处理。

 

以下演示:

DB Version:11.2.0.4

建测试表:

create table scott.tb_60

(

  num NUMBER,

  txt VARCHAR2(10)

);

 

insert into scott.tb_60 values ( 1, 'First' );

insert into scott.tb_60 values ( 2, 'Second' );

insert into scott.tb_60 values ( 3, 'Third' );

 

commit;

 

会话1

更新num=1的记录

update scott.tb_60

set txt='a'

where num=1;

 

会话2

更新num=2的记录

update scott.tb_60

set txt='b'

where num=2;

更新num=1的记录,此时会话2等待会话1提交或回滚来释放num=1上的tx锁

update scott.tb_60

set txt='a'

where num=1;

 

会话3

更新num=3的记录

update scott.tb_60

set txt='c'

where num=3;

更新num=2的记录,此时会话3等待会话2提交或回滚来释放num=2上的tx锁

update scott.tb_60

set txt='b'

where num=2;

 

会话1

更新num=3的记录,此时会话1等待会话3提交或回滚来释放num=3上的tx锁,死锁产生

update scott.tb_60

set txt='c'

where num=3;

报错信息:

ORA-00060: deadlock detected while waiting for resource

 

数据库alert.log中记录错误

ORA-00060: Deadlock detected. More info in file /u01/app/oracle/diag/rdbms/ct6601z3/ct6601z3/trace/ct6601z3_ora_6528.trc.

 

对应的trace file中记录死锁的相关信息

Deadlock graph:

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

Resource Name          process session holds waits  process session holds waits

TX-000a0005-00000c72        27     197     X             28       8           X

TX-0001001a-00000ca7        28       8     X             26     132           X

TX-00030001-00000d4e        26     132     X             27     197           X

 

session 197: DID 0001-001B-00000004     session 8: DID 0001-001C-00000002

session 8: DID 0001-001C-00000002       session 132: DID 0001-001A-00000004

session 132: DID 0001-001A-00000004     session 197: DID 0001-001B-00000004

 

Rows waited on:

  Session 197: obj - rowid = 00038F6D - AAA49tAAEAAABlkAAA

  (dictionary objn - 233325, file - 4, block - 6500, slot - 0)

  Session 8: obj - rowid = 00038F6D - AAA49tAAEAAABlkAAB

  (dictionary objn - 233325, file - 4, block - 6500, slot - 1)

  Session 132: obj - rowid = 00038F6D - AAA49tAAEAAABlkAAC

  (dictionary objn - 233325, file - 4, block - 6500, slot - 2)

...

 

 



RAC全局死锁检测时间  

对于单实例数据库而言,死锁的检测在秒级完成,而RAC环境则死锁的检测时间默认达到了1分钟。

 

 

对于单实例环境如果出现了死锁,那么马上其中一个进程就被中止,用户可以快速的得到错误返回。而对于RAC而言,死锁的检测并不是实时完成,而是需要60秒左右的时间。

会话1执行:

SQL> create table t_deadlock (id number primary key, name varchar2(30));

Table created.

Elapsed: 00:00:00.12
SQL> insert into t_deadlock values (1, 'a');

1 row created.

Elapsed: 00:00:00.00
SQL> insert into t_deadlock values (2, 'b');

1 row created.

Elapsed: 00:00:00.00
SQL> commit;

Commit complete.

Elapsed: 00:00:00.00
SQL> update t_deadlock set name = 'a1' where id = 1;

1 row updated.

Elapsed: 00:00:00.00

会话2执行:

SQL> set timing on
SQL> update t_deadlock set name = 'b2' where id = 2;

1 row updated.

Elapsed: 00:00:00.00
SQL> update t_deadlock set name = 'a2' where id = 1;

此时,会话2等待会话1的最终操作,下面会话1更新被会话2锁定的行,引发死锁:

SQL> update t_deadlock set name = 'b1' where id = 2;
update t_deadlock set name = 'b1' where id = 2
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:01:00.12

可以看到,死锁的超时检测为1分钟。

而这个死锁的检测时间是可以调整的,Oracle通过隐含参数_lm_dd_interval控制:

SQL> conn / as sysdba
Connected.
SQL> alter system set "_lm_dd_interval" = 30 scope = spfile;

System altered.

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area 281018368 bytes
Fixed Size 2095672 bytes
Variable Size 104859080 bytes
Database Buffers 167772160 bytes
Redo Buffers 6291456 bytes
Database mounted.
Database opened.

再次测试死锁的检测时间,会话1

SQL> update t_deadlock set name = 'a1' where id = 1;

1 row updated.

SQL> set timing on

会话2执行更新:

SQL> set timing on
SQL> update t_deadlock set name = 'b2' where id = 2;

1 row updated.

Elapsed: 00:00:00.02
SQL> update t_deadlock set name = 'a2' where id = 1;

会话1执行更新引发死锁:

SQL> update t_deadlock set name = 'b1' where id = 2;

大约30秒后,会话2报错ORA-60

update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:00:30.27

10.2.0.2版本上,Oracle存在一个bug,允许这个参数设置为0,在10.2.0.3以后,这个bug被修正,如果设置为0后,则数据库无法正常启动:

[oracle@node1 ~]$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.5.0 - Production on Mon Jun 4 07:54:09 2012

Copyright (c) 1982, 2010, Oracle. All Rights Reserved.

Connected to an idle instance.

SQL> startup
ORA-00067: invalid value 0 for parameter _lm_dd_interval; must be at least 1

最后修改隐含参数是Oracle不推荐的,而且修改这个参数势必会影响RAC的正常工作方式导致LDM进程的繁忙度增加,而且可能影响RAC环境的稳定性和可用性。

如果确实对于前台的死锁检查时间要求较高,建议在测试环境中详细测试后再部署到产品环境中。

在11g中,该参数默认值为10





深入研究ITL阻塞与ITL死锁  

最近系统死锁连连,google出了很多以前没学过的知识.例如本文,现转载过来备查.

永久链接:http://www.ixdba.com/html/y2007/m05/101-oracle-itl-deadlock.html

 

首先要说的是,这个问题当初我是真遇到过,然后,就有了汪海的这篇文章:

itl deadlock:http://www.orawh.com/80.html

但是NinGoo 在下面回复说,10g模拟不到这样的情况了,开始我没有太在意,后来再想想,9i这么做的确是不对的,因为还有其它的进程可以释放资源,根本还没有达到死锁的条件。那么,10g就没有itl死锁了吗?也不是的,10g也有,不过是改进了一下,需要把所有的进程阻塞住的时候,才能爆发出死锁。

从死锁的原理上来看,10g是正确的,9i是欠完善的。我们现在完整的分析一下itl等待,以及itl死锁的前因后果,因为这部分在我的新书中也有涉及,属于比较难的一部分,先透露出来,免得大家到时候看书可能看得比较糊涂。


1、什么是ITL

ITL(Interested Transaction List)是Oracle数据块内部的一个组成部分,用来记录该块所有发生的事务,一个itl可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个itl的位置就可以被反复使用了,因为itl类似记录,所以,有的时候也叫itl槽位。

如果一个事务一直没有提交,那么,这个事务将一直占用一个itl槽位,itl里面记录了事务信息,回滚段的入口,事务类型等等。如果这个事务已经提交,那么,itl槽位中还保存的有这个事务提交时候的SCN号。如dump一个块,就可以看到itl信息:

    Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
    0x01   0x0006.002.0000158e  0x0080104d.00a1.6e  --U-  734  fsc 0x0000.6c9deff0
    0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

对于已经提交的事务,itl槽位最好不要马上被覆盖,因为一致性读可能会用到这个信息,一致性读的时候,可能需要从这里获得回滚段的入口,并从回滚段中获得一致性读。

itl的个数,受参数initrans控制,最大的itl个数,受maxtrans控制,在一个块内部,默认分配了2个或3个itl的个数,如果这个块内还有空闲空间,那么Oracle是可以利用这些空闲空间并再分配itl的。如果没有了空闲空间,那么,这个块因为不能分配新的itl,所以就可能发生itl等待。

如果在并发量特别大的系统中,最好分配足够的itl个数,其实它并浪费不了太多的空间,或者,设置足够的pctfree,保证itl能扩展,但是pctfree有可能是被行数据给消耗掉的,如update,所以,也有可能导致块内部的空间不够而导致itl等待。


2、ITL等待

我们看一个ITL等待的例子:

  1. Piner@10gR2>create table test(a int) pctfree 0 initrans 1;
  2. Table created.

我们这里指定pctfree为0,initrans为1,就是为了更观察到itl的真实等待情况,那么,现在,我们个这些块内插入数据,把块填满,让它不能有空间分配。

  1. Piner@10gR2>begin
  2.   2       for i in 1..2000 loop
  3.   3         insert into test values(i);
  4.   4        end loop;
  5.   5  end;
  6.   6  /
  7. PL/SQL procedure successfully completed.
  8. Piner@10gR2>commit;
  9. Commit complete.

我们再检查数据填充的情况:

  1. Piner@10gR2>select f,b,count(*) from (
  2.   2     select dbms_rowid.rowid_relative_fno(rowid) f,
  3.   3            dbms_rowid.rowid_block_number(rowid) b
  4.   4            from test) group by f,b;
  5.          F          B   COUNT(*)
  6. ---------- ---------- ----------
  7.          1      29690        734
  8.          1      29691        734
  9.          1      29692        532

可以发现,这2000条数据分布在3个块内部,其中有2个块添满了,一个块是半满的。我们dump一个满的块,可以看到itl信息:

  1. Piner@10gR2>alter system dump datafile 1 block 29690;

回到os,在udump目录下,检查跟踪文件,可以看到如下的信息

    Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
    0x01   0x0006.002.0000158e  0x0080104d.00a1.6e  --U-  734  fsc 0x0000.6c9deff0
    0x02   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000

发现,采用如上参数创建的表,块内部默认有2个itl槽位,如果这里不指定initrans 1,默认是有3个itl槽位的。

因为只有2个ITL槽位,我们可以用三个会话来模拟等待:

会话1,我们更新这个块内部的第一行:

  1. Piner@10gR2>update test set a=a
  2.    2    where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
  3.    3      and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;
  4. 1 row updated.

会话2,我们更新这个块内部的第2行:

  1. Piner@10gR2>update test set a=a
  2.    2    where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
  3.    3      and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;
  4. 1 row updated.

会话3(SID=153),我们更新这个块内部的第三行,发现被阻塞:

  1. Piner@10gR2>update test set a=a
  2.    2    where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
  3.    3      and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;

可以看到,会话被阻塞

观察这个时候的等待事件,我们可以发现是ITL等待:

  1. Piner@10gR2>select EVENT from v$session_wait where sid=153
  2. EVENT
  3. ----------------------------
  4. enq: TX - allocate ITL entry

因为该块只有2个itl槽位,而现在发生了3个事务,而且,因为该块被数据添满,根本没有剩余的空间来分配新的itl,所以发生了等待。如果我们这个实验发生在半满的块29692上面,就发现进程3不会被阻塞,因为这里有足够的空间可以分配新的itl。


3、ITL死锁

那么,理解了itl的阻塞,我们也就可以分析itl的死锁了,因为有阻塞,一般就能发生死锁。还是以上的表,因为有2个itl槽位,我们需要拿2个满的数据块,4个进程来模拟itl死锁:

会话1

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;
  4. 1 row updated.

会话2

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;
  4. 1 row updated.

会话3

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=1;
  4. 1 row updated.

会话4

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=2;
  4. 1 row updated.

以上4个进程把2个不同块的4个itl槽位给消耗光了,现在的情况,就是让他们互相锁住,达成死锁条件,回到会话1,更新块2,注意,以上4个操作,包括以下的操作,更新的根本不是同一行数据,主要是为了防止出现的是TX等待。

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;

发现被阻塞

那我们在会话3,更新块1,当然,也不是同一行

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;

被阻塞


注意,如果是9i,在这里就报死锁了,在进程1,我们可以看到

  1. Piner@9iR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;
  4. update test set a=a
  5.    where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691 
  6.      and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3
  7.        *
  8. ERROR at line 1:
  9. ORA-00060: deadlock detected while waiting for resource

但是,在10g里面,这个时候,死锁是不会发生的,因为这里的进程1还可以等待进程4释放资源,进程3还可以等待进程2释放资源,只要进程2与进程4释放了资源,整个环境又活了,那么我们需要把这两个进程也塞住。

会话2,注意,我们也不是更新的同一行数据

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;

被阻塞

还有最后一个进程,进程4,我们也不更新同一行数据

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29690
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=4;

虽然,以上的每个更新语句,更新的都不是同一个数据行,但是,的确,所有的进程都被阻塞住了,那么,死锁的条件也达到了,马上,我们可以看到,进程1出现提示,死锁:

  1. Piner@10gR2>update test set a=a
  2.    2     where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
  3.    3       and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3;
  4. update test set a=a
  5.    where dbms_rowid.ROWID_BLOCK_NUMBER(rowid)=29691
  6.      and dbms_rowid.ROWID_ROW_NUMBER(rowid)=3
  7.        *
  8. ERROR at line 1:
  9. ORA-00060: deadlock detected while waiting for resource


4、ITL等待与死锁的避免

为了避免以上的情况发生,我们一定要注意在高并发环境下的表中,正确的设置itl个数,如4个,8个等等,保证该块有足够的itl槽位,保证事务能顺利的进行,而没有itl的等待。关于itl的等待,在statspack的段报告中,也能很明显的看到:

    Top 5 ITL Waits per Segment for DB: TEST  Instance: test  Snaps: 13013 -13014
    -> End Segment ITL Waits Threshold:       100

                                               Subobject  Obj.           ITL
    Owner      Tablespace Object Name          Name       Type         Waits  %Total
    ---------- ---------- -------------------- ---------- ----- ------------ -------
    TEST       TBS_EL_IND IDX_LLORDER_ORDERID             INDEX            3   75.00
    TEST       TBS_INDEX2 IDX_AUC_FEED_FDATE              INDEX            1   25.00

如果出现的频率很小,象上面的情况,一般可以不用干预,但是,如果waits很多,则表示这个对象有很严重的itl争用情况,需要增加itl个数。

另外注意的是,有itl等待,并不意味会发生itl死锁,从上面的例子可以看到,发生itl死锁的条件还是瞒苛刻的,如果发生了itl死锁,只能证明,你的系统中,itl等待已经非常严重了。

如果想增加initrans个数,参数可以动态修改,但是,只是针对以后的新块起效,以前的块如果想生效,需要在新参数下,重整表数据,如重建该表,或者move该表。




【DEADLOCK】Oracle“死锁”模拟  
本着实验优先的原则,先模拟死锁的发生,然后在列一下死锁产生的四个必要条件和处理死锁的一般策略。

1.创建两个简单的表t1_deadlock和t2_deadlock,每个表中仅仅包含一个字段a
sys@ora10g> conn sec/sec
Connected.
sec@ora10g> create table t1_deadlock (a int);

Table created.

sec@ora10g> create table t2_deadlock (a int);

Table created.

2.每张表中仅初始化一条数据
sec@ora10g> insert into t1_deadlock values (1);

1 row created.

sec@ora10g> insert into t2_deadlock values (2);

1 row created.

sec@ora10g> commit;

Commit complete.

3.在第一个会话session1中更新表t1_deadlock中的记录“1”为“1000”,不进行提交
sec@ora10g> update t1_deadlock set a = 1000 where a = 1;

1 row updated.

4.在第二个会话session2中更新表t2_deadlock中的记录“2”为“2000”,不进行提交
sec@ora10g> update t2_deadlock set a = 2000 where a = 2;

1 row updated.

5.此时,没有任何问题发生。OK,现在注意一下下面的现象,我们再回到会话session1中,更新t2_deadlock的记录
sec@ora10g> update t2_deadlock set a = 2000 where a = 2;
这里出现了“锁等待”(“阻塞”)的现象,原因很简单,因为在session2中已经对这条数据执行过这个操作,在session2中已经对该行加了行级锁。
注意,这里是“锁等待”,不是“死锁”,注意这两个概念的区别!

检测“锁等待”的方法曾经提到过,请参考《【实验】【LOCK】“锁等待”模拟、诊断及处理方法》http://space.itpub.net/519536/viewspace-605526
sec@ora10g> @lock

lock       lock                
holder   holder         lock               lock request       blocked
username sessid SERIAL# type      id1  id2 mode    mode BLOCK  sessid
-------- ------ ------- ------ ------ ---- ---- ------- ----- -------
SEC         141    6921 TM      15160    0    3       0     0
SEC         141    6921 TX     393231 1672    6       0     1     145
SEC         145    7402 TM      15159    0    3       0     0
SEC         145    7402 TM      15160    0    3       0     0
SEC         145    7402 TX     131077 1675    6       0     0
            164       1 TS          3    1    3       0     0
            165       1 CF          0    0    2       0     0
            165       1 RS         25    1    2       0     0
            165       1 XR          4    0    1       0     0
            166       1 RT          1    0    6       0     0
            167       1 PW          1    0    3       0     0

11 rows selected.


6.我们关注的“死锁”马上就要隆重出场了:在会话session2中,更新t1_deadlock的记录(满足了死锁产生的四个条件了,请慢慢体会)
sec@ora10g> update t1_deadlock set a = 1000 where a = 1;
这里还是长时间等待的现象,但是这里发生了“死锁”!!
细心的您再去第一个会话session1中看一下,原先一直在等待的SQL语句出现如下的现象:
sec@ora10g> update t2_deadlock set a = 2000 where a = 2;
update t2_deadlock set a = 2000 where a = 2
       *
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource

更进一步:查看一下alert警告日志文件发现有如下的记录
Mon Aug 10 11:24:29 2009
ORA-00060: Deadlock detected. More info in file /oracle/app/oracle/admin/ora10g/udump/ora10g_ora_25466.trc.

再进一步:看看系统自动生成的trace文件中记录了什么
这个文件包含了5721行的记录信息,截取其中我们关心的前面N多行的内容(结合刚才检测“锁等待”脚本产生的结果分析一下,看看有没有收获):
/oracle/app/oracle/admin/ora10g/udump/ora10g_ora_25466.trc
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit Production
With the Partitioning, Oracle Label Security, OLAP and Data Mining Scoring Engine options
ORACLE_HOME = /oracle/app/oracle/product/10.2.0/db_1
System name:    Linux
Node name:      testdb
Release:        2.6.18-53.el5xen
Version:        #1 SMP Wed Oct 10 16:48:44 EDT 2007
Machine:        x86_64
Instance name: ora10g
Redo thread mounted by this instance: 1
Oracle process number: 14
Unix process pid: 25466, image: oracle@testdb (TNS V1-V3)

*** 2009-08-10 11:24:29.541
*** ACTION NAME:() 2009-08-10 11:24:29.540
*** MODULE NAME:(SQL*Plus) 2009-08-10 11:24:29.540
*** SERVICE NAME:(SYS$USERS) 2009-08-10 11:24:29.540
*** SESSION ID:(145.7402) 2009-08-10 11:24:29.540
DEADLOCK DETECTED
[Transaction Deadlock]
Current SQL statement for this session:
update t2_deadlock set a = 2000 where a = 2
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-00020005-0000068b        14     145     X             15     141           X
TX-0006000f-00000688        15     141     X             14     145           X
session 145: DID 0001-000E-0000037D     session 141: DID 0001-000F-0000013D
session 141: DID 0001-000F-0000013D     session 145: DID 0001-000E-0000037D
Rows waited on:

7.以上种种现象说明什么?
说明:Oracle对于“死锁”是要做处理的,而不是采用下面提到的“鸵鸟算法”不闻不问。
注意trace文件中的一行如下提示信息,说明一般情况下都是应用和人为的,和Oracle同学没有关系:
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.

8.以上演示了一种“死锁”现象的发生,当然导致死锁发生的情况远远不仅如此。所以在程序设计时一定要好好的进行思考

9.【拓展】
死锁产生的四个必要条件
1)Mutual exclusion(互斥):资源不能被共享,只能由一个进程使用。
2)Hold and wait(请求并保持):已经得到资源的进程可以再次申请新的资源。
3)No pre-emption(不可剥夺):已经分配的资源不能从相应的进程中被强制地剥夺。
4)Circular wait(循环等待条件):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。

处理死锁的一般策略
1)鸵鸟算法忽略该问题
2)检测死锁并且恢复
3)仔细地对资源进行动态分配,以避免死锁
4)通过破坏死锁产生呢过的四个必要条件之一,来防止死锁产生

10.总结
死锁对于数据库来说是非常要命的,请多多注意!
对于上面的演示处理的方式:在会话session1中执行rollback进行回滚,会释放导致session2锁等待的锁资源(死锁Oracle已经处理了)。



  自相矛盾:一个进程可以自成死锁么  

Oracle | 2016-02-03 07:58

崔华,网名 dbsnake

Oracle ACE Director,ACOUG 核心专家

编辑手记:感谢崔华授权我们独家转载其精品文章,也欢迎大家向“Oracle”社区投稿。在新年前,轻松一点,看看崔华这篇小文,通过一个简单的例子,理解Oracle的自制事务、死锁,建议大家动手去测试、尝试,从而从中学到更多的知识。

有朋友问我:“一个transaction会自我死锁吗?也就是自己锁死了自己”。

很凑巧,半个月前我刚好帮同事处理过这种自我死锁的情况。

我们这里来构造一个自我死锁的例子:

select sid from v$mystat

where rownum<2;

SID

———-

362

SQL> create table t1 (id varchar2(10),

amount number(10));

Table created

SQL> insert into t1 values('cuihua',100);

1 row inserted

SQL> commit;

Commit complete

SQL> select * from t1;

ID AMOUNT

———- ———–

cuihua 100

SQL> create procedure p_autonomous is

2 PRAGMA AUTONOMOUS_TRANSACTION;

3 begin

4 update t1 set amount=102

5 where id='cuihua';

6 commit;

7 end;

8 /

Procedure created

SQL> create procedure p_test is

2 begin

3 update t1 set amount=101 where id='cuihua';

4 p_autonomous;

5 commit;

6 end;

7 /

Procedure created

现在只要我执行上述存储过程p_test,就会产生自我死锁,如下所示:

wpsF803.tmp

此时alert log里会显示:

ORA-00060: Deadlock detected.

More info in file /u01/app/oracle/admin/ipra/udump/ipra_ora_921828.trc.

从上述trace文件里我们可以看到:

wpsF814.tmp

也就是说这里的Blocker是session 362,Waiter也是session 362,典型的自己锁死了自己。

不知道我为什么要这样构造的朋友们看了如下这样一段话就什么都明白了:

TheOracle server provides the ability to temporarily suspend a current transaction and begin another. This second transaction is known as an autonomous transactionand runs independently of its parent. The autonomous or child transaction can commit or rollback as applicable, with the execution of the parent transaction being resumed upon its completion.

The parent may then perform further operations and commit or roll back without affecting the outcome of any operations performed within the child. The child transaction does not inherit transaction context (that is, SET TRANSACTION statements). The transactions are organized as a stack: Only the “top” transaction is accessible at any given time. Once completed, the autonomous transaction is “popped” and the calling transaction is again visible. The limit to the number of suspended transactions is governed by the initialization parameter TRANSACTIONS.

The Oracle server uses similar functionality internally in recursive transactions.

Transactions must be explicitly committed or rolled back or an error ORA-6519 is signaled when attempting to return from the autonomous block.

A deadlock situation may occur where a called and calling transaction deadlock; — this is not prevented, but is signaled by an error unique to this situation. The application developer is responsible for avoiding this situation.








About Me

...............................................................................................................................

● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用

● 本文在itpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新

● 本文itpub地址:http://blog.itpub.net/26736162/viewspace-2127247/

● 本文博客园地址:http://www.cnblogs.com/lhrbest/p/6005702.html

● 本文pdf版及小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/

● 数据库笔试面试题库及解答:http://blog.itpub.net/26736162/viewspace-2134706/

● QQ群:230161599     微信群:私聊

● 联系我请加QQ好友(646634621),注明添加缘由

● 于 2016-09-01 15:00 ~ 2016-10-20 19:00 在魔都完成

● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解

● 版权所有,欢迎分享本文,转载请保留出处

...............................................................................................................................

拿起手机使用微信客户端扫描下边的左边图片来关注小麦苗的微信公众号:xiaomaimiaolhr,扫描右边的二维码加入小麦苗的QQ群,学习最实用的数据库技术。


DBA笔试面试讲解
欢迎与我联系

相关实践学习
阿里云百炼xAnalyticDB PostgreSQL构建AIGC应用
通过该实验体验在阿里云百炼中构建企业专属知识库构建及应用全流程。同时体验使用ADB-PG向量检索引擎提供专属安全存储,保障企业数据隐私安全。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
目录
相关文章
|
监控 Oracle 安全
Oracle数据库用户频繁被锁问题原因排查及解决
由于应用环境下Oracle用户总是频繁被锁,经常不能执行数据库事务操作,严重影响了系统运行效率。通过问题原因分析及排查,发现了原因,在此记录一下。
4590 0
Oracle数据库用户频繁被锁问题原因排查及解决
|
存储 Oracle 关系型数据库
Oracle中“ORA-00060: 等待资源时检测到死锁” 或存储过程编译卡死的一种解决方法
Oracle中“ORA-00060: 等待资源时检测到死锁” 或存储过程编译卡死的一种解决方法
813 0
|
4月前
|
Oracle 关系型数据库 MySQL
Mysql和Oracle数据库死锁查看以及解决
【8月更文挑战第11天】本文介绍了解决MySQL与Oracle数据库死锁的方法。MySQL可通过`SHOW ENGINE INNODB STATUS`查看死锁详情,并自动回滚一个事务解除死锁;也可手动KILL事务。Oracle则通过查询V$LOCK与V$SESSION视图定位死锁,并用`ALTER SYSTEM KILL SESSION`命令终止相关会话。预防措施包括遵循ACID原则、优化索引及拆分大型事务。
147 3
|
5月前
|
Oracle 关系型数据库 数据库
关系型数据库Oracle死锁
【7月更文挑战第16天】
71 3
|
7月前
|
SQL Oracle 关系型数据库
【ORACLE】 事务 | 锁 | 约束 | 权限、角色与用户管理
【ORACLE】 事务 | 锁 | 约束 | 权限、角色与用户管理
75 1
|
7月前
|
SQL Oracle 安全
Oracle数据库中的事务和锁
【4月更文挑战第19天】Oracle数据库的事务和锁是确保数据完整性和并发控制的核心机制。事务遵循ACID原则,保证操作的原子性、一致性、隔离性和持久性。通过COMMIT或ROLLBACK来管理事务更改。锁包括共享锁(读)、排他锁(写)、行级锁和表级锁,用于控制并发访问。自动锁机制在DML操作时生效,防止数据冲突。事务和锁共同维护数据库的稳定和安全。
|
7月前
|
SQL Oracle 关系型数据库
Oracle-锁解读
Oracle-锁解读
99 0
|
SQL 缓存 监控
Oracle 死锁与慢查询总结
Oracle 死锁与慢查询总结
492 0
|
运维 Oracle 关系型数据库
Oracle优化02-锁和阻塞
Oracle优化02-锁和阻塞
128 0
|
SQL 存储 Oracle
Oracle事务和锁机制
Oracle事务和锁机制
138 0