死锁oracle rac,利用LOGMINER进行RAC环境下的死锁分析——转载

RAC中的死锁的判断机制跟单机很不相同,比单机要复杂的多,而且消耗的时间和资源也比单机要多的多,所以亚马逊的DBA

TEAM曾经在一份经验总结中指出如果是并发非常大的OLTP系统,如果锁的问题处理不好,那可能是个恶梦。这个测试其实很简单,找一个RAC的机器,自己模拟一个死锁,然后等到ORACLE自动检测并解开一个锁,大多数时候需要消耗1分钟甚至更长的时间,无论发生死锁的SESSION是在同一个节点还是在不同的节点(而单机的时候死锁检测是非常快的,基本上一发生资源相互等待,立马会报一个死锁出来)。如果事务非常频繁,而一个死锁检测需要消耗1分钟时间的话,那立马会引起后面的SESSION的锁等待,假如两个SESSION

LOCK住的资源上每秒钟有100个TRANSACTION发生,那等1分钟后锁自动解开的时候,已经有6000个SESSION堆积起来了,这会引起数据库端的SESSION数目急剧上升,也可能会超过数据库的最大SESSION数目的限制,也许这些会话会导致前端WEB或者AS连接池爆满从而导致后面的请求不能进来等等一堆问题,总之,这是个很可怕的事!

最根本的原因是在锁的检测机制上,在RAC环境下,是由LMD进程来统一管理各个节点的锁资源的,至于为什么检测一个死锁需要这么长时间不是这篇文章探讨的重点,这篇文章主要探讨的是如何来找到锁发生的原因并解决掉这个问题。而且发生死锁的情况不一定完全是因为资源的交叉,也有BITMAP死锁、ITL死锁、外键死锁等多种(这个可以参考http://zhang41082.itpub.net/post/7167/301109和http://zhang41082.itpub.net/post/7167/302927),这里只探讨资源交叉等待发生的死锁,以下提到的死锁专门指资源交叉等待的这种死锁。

单机情况下的死锁处理起来比较简单,一般都会生成_ORA_.TRC类似的TRACE文件,文件中会详细的记录发生死锁时的SESSION相关的情况,包括执行什么SQL,这个SQL如果是绑定变量的,那可以根据发生锁等待的资源的ROWID来找到具体等待哪一行的数据,从而根据业务逻辑,找到死锁在程序中发生的位置。但如果是RAC环境,那这个TRACE文件则是_LMD_.TRC这样的格式的,其中保留了发生死锁时DUMP出来的相关信息,但是这些信息没有单机时候那么详细。那么,可以根据发生死锁时的事务的XID来进行LOGMINER,找到当时整个事务发生时执行的所有相关SQL,从而进行分析和定位(LOGMINER的使用这里不作介绍)。

下面首先来看一个RAC下死锁发生时的TRACE文件:

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

BLOCKED 0xc5ab67540 5 [0x8002b][0x2bc9d9],[TX]

[119000-0003-000658C4] 2

BLOCKER 0xbc5fb5438 5 [0x8002b][0x2bc9d9],[TX]

[DB000-0001-00002720] 0

BLOCKED 0xc5ad5b040 5 [0x413000b][0xaf44b],[TX]

[DB000-0001-00002720] 0

BLOCKER 0xbd1873960 5 [0x413000b][0xaf44b],[TX]

[119000-0003-000658C4] 2

从METALINK上得到WFG的格式如下:

不过之前版本的确实是这个格式,虽然METALINK上说这个格式从8-11版本都适合的,但是搞不清除倒数第二列的数字是啥意思,那么我们这里需要用到的是4、5和最后一列,这4、5列定义了一个事务唯一的XID,最后一列表示这个事务是发生在哪个节点上的,其中这个是从0开始的,所以0表示第一个节点,2表示第三个节点。

从上面我们可以得到两串数字:

[0x8002b][0x2bc9d9]

[0x413000b][0xaf44b]

这两个就是两个事务的XID,那么根据根据TRACE产生的时间点,我们可以得到这个死锁发生的时候的日志是记录在哪个SEQUENCE的日志中,如果日志已经归档,则需要LOGMINER相应的归档日志,如果还没有归档,则需要LOGMINER当前日志,另外注意,因为死锁发生在1和3两个节点上,所以需要把两个节点上对应的日志同时LOGMINER。然后使用下面的SQL进行查询:

SELECT *

FROM V$LOGMNR_CONTENTS

WHERE (XIDUSN = TO_NUMBER(SUBSTR('040d002e', 1, 4), 'XXXX')

AND

XIDSLT = TO_NUMBER(SUBSTR('040d002e', 5, 8), 'XXXX') AND

XIDSQN = TO_NUMBER('9d0d8', 'XXXXXXXX'))

OR (XIDUSN = TO_NUMBER(SUBSTR('0008002b', 1, 4), 'XXXX') AND

XIDSLT = TO_NUMBER(SUBSTR('0008002b', 5, 8), 'XXXX') AND

XIDSQN = TO_NUMBER('2bc9d9', 'XXXXXXXX'))

ORDER BY 1, RS_ID, SSN;

得到的就是事务发生时执行的所有SQL,从而可以很容易的定位到程序中相应的模块。

其中SIDUSN和XIDSLT是由XID的前半部分转换而来的,不足8位的左边补0够8位;XIDSQN是由XID的后半部分转换得到的,这部分也是8位左补0的,因为这块是否补0对结果没有影响,所以这里简单期间不补0。

根据ORACLE

DOCUMENT,V$LOGMNR_CONTENTS中的每个记录是按照RS_ID和SSN组合来标识唯一的,翻了半天也没有找到哪个字段表示SQL执行的顺序,经过简单测试和猜测,如果是同一个节点的,则可以认为RS_ID和SSN排序就是SQL执行的顺序,如果是跨节点的,因为有不同的THREAD来写REDO

LOG,所以排序的时候需要加上第一列,也就是SCN列。

根据以上的分析和猜测,最终定位到了死锁发生的原因。那么解决很简单,所以涉及到交叉锁等待的地方,都对更新条件进行排序,使得所有的更新都按照相同的顺序来进行,从而避免交叉,也就避免了交叉等待的死锁。

update:

RAC环境下死锁发生的时候,在系统的BDUMP目录下的DIAG进程的TRACE文件中也会有相关的记录的。首先ps

-ef|grep

diag找到当前实例的DIAG进程的操作系统进程号,然后根据_diag_.trc来找到对应的TRACE文件,然后翻到死锁发生的时间点,开始搜索rowid,将会得到死锁发生时等待的记录的OBJECT_ID和ROWID,其中OBJECT_ID是16进制,转换成10进制后到DBA_OBJECT视图中根据OBJECT_ID进行查询,可以得到锁是加在哪个对象上的,然后根据ROWID就可以直接定位到死锁发生时的锁到底加在哪行记录上的,然后根据数据的业务特点来进行程序代码上的分析,将会更容易些(比如我时绑定变量的SQL,同样的SQL可能对应几十种类型的交易,那如果知道锁具体在哪条记录上,就可以知道交易的类型,在业务上和程序代码上就更容易定位了)。

另外补充一点,RAC环境下的死锁检测机制为什么这么慢呢?

因为RAC环境下是需要所有节点的进行互相通讯和判断才能完成死锁的检测,而这个任务是交给LMD进程来完成的,而系统默认情况下,这个进程60秒才出来干活一次,所以死锁的检测和解开至少要60秒以上了,这个也从理论上支持了之前的实验。

这个缺省值是通过_lm_dd_interval,一个隐含参数来控制的,可以调整这个值的大小来调整LMD干活的频率,但是经过测试,调整为120的时候,确实检测到死锁需要120秒;调整为10,则需要将近20秒;调整为1,则还是需要70多秒。可见ORACLE设置了这个值为60肯定是有道理的,如果设置的过小肯定也起不了多大的作用的(否则ORACLE为啥要让这个检测这么慢从而影响系统运营呢,那肯定有苦衷的),并且这个参数是隐含的,那最好还是不要随便修改,带来的后果可能是LMD进程通讯太频繁导致占用过多CPU,也可能LMD调度频率跟不上而出现系统不稳定等等。

生产系统,解决死锁才是王道,修改隐含参数是歪门邪道,要禁止。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值