首页 > DPOS技术, EBS-DBA > Oracle死锁问题小结

Oracle死锁问题小结

2008年11月22日 admin 发表评论 阅读评论

 

Oracle 有三种常见的死锁:
1)ORA-60, enqueue deadlocks;
2)ORA-4020, library cache deadlocks;
3)Deadlocks in RAC

一、ORA-60, enqueue deadlocks

这种死锁很容易重现,按照如下步骤操作:
In sessionA, update tb set col = 'x' where pk = 'aa'
In sessionB, update tb set col = 'x' where pk = 'bb'
In sessionA, update tb set col = 'x' where pk = 'bb'
In sessionB, update tb set col = 'x' where pk = 'aa'

如果两个Session没有commit,那么在上面最后一句sql执行后3秒后,SessionA就会抛出error ORA-60,并且在udump trace 文件中产生死锁提示信息:

                        ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TX-00010017-000000ba         8       7     X              9       8           X
TX-00020012-000000af         9       8     X              8       7           X
session 7: DID 0001-0008-00000002	session 8: DID 0001-0009-00000002
session 8: DID 0001-0009-00000002	session 7: DID 0001-0008-00000002
Rows waited on:
Session 8: obj - rowid = 00000CBB - AAAAzjAADAAAAFDAAA
Session 7: obj - rowid = 00000CBB - AAAAzjAADAAAAFDAAB

解释:
1)Resource Name(资源) 其实是指的锁资源(锁是一种资源),在v$resource中可以查询。

2)TX-00020012-000000af
TX 代表事务(transaction),通常指行锁。00020012 和 000000af 分别对应v$lock.id1, v$lock.id2 是16进制的。比如 20012[hex] 是 131090; af[hex] 是 175
2.1) v$lock.id1 表示 回滚段号码左移16位,与slot做或操作得到的结果 "undo segment number << 16 | slot"
确实,我们发现:
v$transaction.xidusn=2 (xidusn =Tx id undo segment number).
x$transaction.xidslot = 18.
所以把十进制的2转换成2进制是10,10左移16位 并且与18做或者操作 等于 131090,计算过程如下:

       100000000000000000(10左移16)
                    10010(18的二进制)
       100000000000010010(上述两个数字或操作结果,你可以在计算器中转成10进制,它=131090)

2.2) v$lock.id2 应该是 "sequence" 我们发现 v$transaction.xidsqn = 175(xidsqn =Tx id sequence number).

3)Process 是指Oracle process ID(不要和操作系统层的进程ID混淆,它既不是unix上的process id 也不是windows 上的thread ID)
它是v$process.pid 在v$process中,v$process.addr 和 v$session.paddr 做关联

4)session 是 v$session.sid,

5)holds,waits : X表示它现在正hold 了一个排它锁(hold=X),或者正在等待一个排它锁(wait=X)。

6)DID是某种资源ID,第一段可能是instance ID(对于single instance database 它总是1),第2段可能是Oracle 的process id,第3段总是2(除了在 SNP jobprocess 的dump文件中, 不太确定这个段的具体含义)

7)Rows waited on: 对于等待行锁的session, 关于这个行的额外信息就在这段提供。

7.1) CBB[hex] 是 dba_objects.object_id.
7.2) rowid , 这个大家就比较熟悉了,我们通常可以通过它定位到一条记录:SELECT * FROM thetable WHERE ROWID = 'rowid'
7.3) 如果Rows waited on:下面跟着"no rows",那么表示它在等待一个表锁,而不是行锁,象下面这样:

                               ---------Blocker(s)--------  ---------Waiter(s)---------
       Resource Name          process session holds waits  process session holds waits
       TX-00060004-00003155        41      45    X            15        26          S
       TX-0012000b-00002802        15      26    X            41        45          S
       session 45: DID 0001-0029-00000002 session 26: DID 0001-000F-00000002
       session 26: DID 0001-000F-00000002 session 45: DID 0001-0029-00000002
       Rows waited on:
       Session 26: no row
       Session 45: no row

紧跟着死锁图表(deadlock graph 就指上面提供的死锁信息表)的是process 状态导出信息,这些信息以缩进的格式显示状态对象(state object :so)的层次关系。

补救措施:

1)如果 Rows waited on: 下面的Session 后面给出了row ids ,那么只需要简单的等待并退出应用程序,然后考虑是否你有两支程序以不同的顺序操作更新同一张表?
2)如果 Rows waited on: 下面的Session 后面给除了"no row",呢么这个错误可能是多个session试图更新同一个块(block),而此时这个block的剩余空间太小,Oracle 正在自动增加ITLs((interested transaction lists),针对这种情况,你可能使用更高的INITRANS重建这张表,并设定比较高的PCTFREE。

另一种经常发生的可能性是:你在子表的外健上所在的列上没有索引,并且多个session正在更新父表的主键字段 或者是正在从父表中删除行。两个session 都试图获取子表上共享的表锁 并且它们已经hold了一个行锁(在不同的行上),这种情况下就会导致死锁。
在这种情况下,你可以在子表的外键字段上建立索引,很多站点都提供了脚本来发现没有建立索引的外键(比如:http://www.jlcomp.demon.co.uk/faq/fk_ind.html)
如果你不想这么做,请确保你还没有并且也不打算删除父表中的记录(更新父表的主键是很少见的)或者就象steve adams 建议的那样:ALTER TABLE childtable DISABLE TABLE LOCK. 更多信息可参考:Metalink Note:62365.1

二、ORA-4020, library cache deadlocks

重现方法:按照如下步骤操作:

SQL&gt; create procedure p1 as begin p2; end;
  2  /
 
Warning: Procedure created with compilation errors.
 
SQL&gt; create procedure p2 as begin p1; end;
  2  /
 
Warning: Procedure created with compilation errors.
 
SQL&gt; alter procedure p1 compile;
alter procedure p1 compile
*
ERROR at line 1:
ORA-04020: deadlock detected while trying to lock object YONG.P1

上述例子是从Connor McDonald 的"Mastering Oracle PL/SQL: Practical Solutions" 改变而来的,
很显然,在实际应用中,在写代码的过程中,你无法构造出这种彼此调用的代码中而产生的library catch锁。
通常,这种错误是因为无效的对象特别是Trigger,或者Oracle bug 抛出的。E.g., Jonathan Lewis在他的"Practical Oracle8i" 曾报道过这样的例子:
当时正在做partitioned IOT的维护,一个查询抛出了ORA-4020的死锁信息,如下:

ORA-04020: deadlock detected while trying to lock object YONG.P1
--------------------------------------------------------
  object   waiting  waiting       blocking blocking
  handle   session     lock mode   session     lock mode
--------  -------- -------- ----  -------- -------- ----
797992F8  7AA06C48 79FA5554    X  7AA06C48 79F8B3D0    X
--------------------------------------------------------

注意在这个案例中, waiting session 和 blocking session 是同一个,因为它们的Session 地址是一样的.

补救措施:

如果死锁是由于同一个Session引起的,请检查你的死锁的Trace 文件中涉及的表上是否有无效的对象,比如无效的Trigger,如果确实存在,手工编译这些对象.
对于这种死锁,进行event 4020 Trace跟踪将有助于获取更多内部信息,命令如下:
alter session set events '4020 trace name processstate forever, level 10';
如果你不知道或者无法访问这个Session,那么在init.ora文件中添加:
event='4020 trace name processstate forever, level 10' ,并重新启动数据库.
那么当下次发生死锁的时候就会产生trace文件,里面应该类似这样的字符串"status=INVL". 查看"name="后面的对象名.
如果你经常遇到ORA-4020错误,到metalink寻求帮助或者提个tar.

三、Deadlocks in RAC

在alert.log 中没有具体的错误号,但是ORA-60 and ORA-4020 都有可能。我们只是在alert.log中看到:
"Global Enqueue Services Deadlock detected. More info in file /home/oracle/admin//bdump/_lmd0_
.trc."
trace文件中显示如下内容:

user session for deadlock lock 0x7553ab14
  pid=33 serial=37657 audsid=6450105 user: 246/
 
  O/S info: user: , term: MISFBB10-54, ospid: 4148:5156, machine: \
 
            program: sqlplus.exe
  application name: SQL*Plus, hash value=3669949024
  Current SQL Statement:
  update test set a = :"SYS_B_0" where a = :"SYS_B_1"
Global Wait-For-Graph(WFG) at ddTS[0.28] :
BLOCKED 0x7553ab14 5 [0xf001d][0x8353],[TX] [2162689,7995] 0
BLOCKER 0x69cabb5c 5 [0xf001d][0x8353],[TX] [1769474,4478] 1
BLOCKED 0x69cabc30 5 [0x70014][0xe908],[TX] [1769474,4478] 1
BLOCKER 0x7e1424e8 5 [0x70014][0xe908],[TX] [2162689,7995]

根据Note:262226.1 ,这种情况曾经发生在这样的背景下:
OS:linux
Oracle version: 9.2.0.7.0 RAC
客户端程序所在的OS:windows pc

解读
Global Wait-For-Graph(WFG) at ddTS[0.28] : 下面有4行信息,解读如下:

行号 阻塞/被阻塞 Lock pointers 锁模式 id1      id2      锁名称 transaction_id0,id1 DB实例
1    BLOCKED     0x7553ab14    5      [0xf001d][0x8353],[TX]   [2162689,7995]      0
2    BLOCKER     0x69cabb5c    5      [0xf001d][0x8353],[TX]   [1769474,4478]      1
3    BLOCKED     0x69cabc30    5      [0x70014][0xe908],[TX]   [1769474,4478]      1
4    BLOCKER     0x7e1424e8    5      [0x70014][0xe908],[TX]   [2162689,7995]

Note:262226.1 解释了相关意义:
1)Lock Name(TX)后面的数值意义:比如 [2162689,7995] 方括号中的两个数字是 v$ges_blocking_enqueue中的transaction_id0 和 transaction_id1 ,分别等效于v$dlm_locks中的相应的列。(但我不知道transaction number 的意义。这看起来不像是v$transaction中 xidusn.xidslot.xidsqn的组合)
2)此外"BLOCKED" 或者 "BLOCKER"后面跟的十六进制数值是v$ges_blocking_enqueue中的handles (lock pointers),
3)锁模式后面的两个十六进制数值分别是 v$lock中的id1和id2(请参考http://yong321.freeshell.org/oranotes/LocksWhere2FindMoreInfo.txt 理解它们的意义 )

上述案例说明了这样一个事实:
instance 0上的第一个进程请求一个mode5的Tx lock [note2](第1行),并且被instance 1上的第2个进程阻塞(第2行),
而instance 1上的第2个进程又请求一个TX锁(第3行),而这个锁有反过来被instance 0上的第一个进程阻塞(第4行)

上面的trace文件显示了死锁中的SQL,但在有些案例中是没有SQL的[note3],如下面的例子:

Global Wait-For-Graph(WFG) at ddTS[0.15] :
BLOCKED 0x70d17058 5 [0xf4dd2a46][0x57e4077],[LB] 30268 0
BLOCKER 0x70d16594 5 [0xf4dd2a46][0x57e4077],[LB] 30268 0

这个死锁是“B-namespace library cache”锁请求,并且被自己阻塞。process id是30268(这里是单个数值,不是在方扩号中的两个数值),很不幸,这里丢失了SQL
Note:262226.1 指出添加SQL到WFG trace将是有帮助的,如果死锁可以在同一instance中重现(正如这个案例中,最后一列(instance)是相同的),可以先取消数据库集群,
在非RAC环境中的死锁信息比global WFG更加有帮助,因为这是在所有相关的进程都在同一节点的时候创建的。

备注:
________________________

[note1]
我们经常建议应用程序写代码的时候应该按照相同的顺序对表进行DML操作。也就是说,程序1应该先更新tab1X 然后再更新tb1Y, 而程序2也应该按照相同的顺序进行更新。
但是还有很多其他情况,也应该遵循这个规则。考虑如下情况:

if conditionA, update tblX
if conditionB, update tblY
if conditionC, update tblX
if conditionD, update tblY

如果这个单一程序(没有程序2)是可能在多个进程中跑的,根据每个进程遇到的判断条件不一样,也可能引发死锁。

[note2]
我还从来没见过在Global WFG's中,锁模式不等于5的情况,这个数值可能Oracle 代码中偶尔也存在的hardcoded情况吗?

[note3]
根据 Dusan Bolek (message 10 of
http://groups.google.com/group/comp.databases.oracle.server/browse_frm/thread/a1e9aacfc4e16e25/),
如果死锁发生在多哥instance之间,那么SQL就会被提供,而在同一个instance上的话就没有提供。
编注:
______________
这篇小结是在美国工作的黄勇写的,我这里只是把他翻译一下。
原文参考:http://yong321.freeshell.org/computer/deadlocks.txt
有关锁的概念还可以参考: http://www.4studio.cn/www/51/2008-04/2672.html

 

 

关于作者:

昵称:admin
档案信息:商云方 (yunfang.shang@hand-china.com), HAND技术服务中心
联系方式:你可以通过syfvb@hotmail.com联系作者
点击查看admin发表过的所有文章...
本文永久链接: http://blog.retailsolution.cn/archives/2069

 

 

对本文的评价:

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5.00 out of 5)
Loading ... Loading ...

 

 

  1. 本文目前尚无任何评论.
  1. 2014年5月5日22:34 | #1
您必须在 登录 后才能发布评论.