一个项目上的数据库宕掉了,开始非说是我的工作流造成的。我也不是数据库的专家,只能硬着头皮边学边用的查了,怀疑是数据库死锁,就沿着这个方向前进。
首先sa登录查询分析器,执行Dbcc traceon(1204, 3605, -1),其中参数1204代表返回参与死锁的锁的类型以及当前受影响的命令,3605代表trace到的信息必须记录到日志中,-1代表返回当前所有打开的跟踪标记的状态。有几点要注意,1、事件探查器是跟不出死锁的sql的,因为它只是记录执行过的sql,而这些在事务里面死锁的sql都会被回滚的;2、只有在打开这些标记后数据库才会记录日志,打开这些标记会影响数据库性能,记得用完了使用Dbcc traceoff(标记号)关掉。
接着就是再现这个死锁了,让实施人员在远程找5、6个人同时点这个功能,嘿嘿,不错,一下子就出来了。将数据库日志发过来,一般在Program Files\Microsoft SQL Server\MSSQL.4\MSSQL\LOG\ERRORLOG下。打开文件仔细看发现下面的日志。
2008-05-31 10:10:46.20 spid5s Node:1
2008-05-31 10:10:46.20 spid5s RID: 5:1:6815:21 CleanCnt:3 Mode:X Flags: 0x2
2008-05-31 10:10:46.20 spid5s Grant List 2:
2008-05-31 10:10:46.20 spid5s Owner:0x3711E420 Mode: X Flg:0x0 Ref:1 Life:02000000 SPID:59 ECID:0 XactLockInfo: 0x387A26FC
2008-05-31 10:10:46.20 spid5s SPID: 59 ECID: 0 Statement Type: DELETE Line #: 133
2008-05-31 10:10:46.20 spid5s Input Buf: RPC Event: Proc [Database Id = 5 Object Id = 1254295528]
2008-05-31 10:10:46.20 spid5s Requested By:
2008-05-31 10:10:46.20 spid5s ResType:LockOwner Stype:'OR'Xdes:0x533EBC80 Mode: S SPID:67 BatchID:0 ECID:0 TaskProxy:(0x1F092374) Value:0x2962ae40 Cost:(0/0)
2008-05-31 10:10:46.20 spid5s
2008-05-31 10:10:46.20 spid5s Node:2
2008-05-31 10:10:46.20 spid5s KEY: 5:72057594056212480 (fc0199b9be7a) CleanCnt:2 Mode:S Flags: 0x0
2008-05-31 10:10:46.20 spid5s Grant List 2:
2008-05-31 10:10:46.20 spid5s Owner:0x2028AD80 Mode: S Flg:0x0 Ref:1 Life:00000000 SPID:67 ECID:0 XactLockInfo: 0x533EBCA4
2008-05-31 10:10:46.20 spid5s SPID: 67 ECID: 0 Statement Type: SELECT Line #: 1
2008-05-31 10:10:46.20 spid5s Input Buf: RPC Event: Proc [Database Id = 5 Object Id = 484053356]
2008-05-31 10:10:46.20 spid5s Requested By:
2008-05-31 10:10:46.20 spid5s ResType:LockOwner Stype:'OR'Xdes:0x387A26D8 Mode: X SPID:59 BatchID:0 ECID:0 TaskProxy:(0x576FE374) Value:0x1eb9ad20 Cost:(0/84)
2008-05-31 10:10:46.20 spid5s
2008-05-31 10:10:46.20 spid5s Victim Resource Owner:
2008-05-31 10:10:46.20 spid5s ResType:LockOwner Stype:'OR'Xdes:0x533EBC80 Mode: S SPID:67 BatchID:0 ECID:0 TaskProxy:(0x1F092374) Value:0x2962ae40 Cost:(0/0)
Node1(SPID:59)拥有了一个资源的排他锁Mode: X ,而需要申请另一个资源的共享锁Mode: S,Node2(SPID:67)拥有了后一个资源的共享锁,请求前一个资源的排他锁。根据学校里的零星知识,还是能明白这个道理的。照理说这种情况是不会死锁的,但是如果在事务里,共享锁也会在事务结束以后才释放。
联机丛书上有一些信息可以利用来看这段日志。
类型列
"类型"列显示当前锁定的资源类型。
资源类型
描述
RID
用于锁定表中的一行的行标识符。
KEY
索引中的行锁。用于保护可串行事务中的键范围。
PAG
数据或索引页。
EXT
相邻的八个数据页或索引页构成的一组。
TAB
包括所有数据和索引在内的整个表。
DB
数据库。
资源列
"资源"列提供有关正被锁定资源的信息。
资源类型
描述
RID
表内已锁定行的行标识符。行由 fileid:page:rid 组合进行标识,其中,rid 是页中的行标识符。
KEY
SQL Server 内部使用的十六进制数字。
PAG
页码。页由 fileid:page 组合进行标识,其中,fileid 是 sysfiles 表中的 fileid,而 page 是该文件内的逻辑页码。
EXT
正被锁定的扩展盘区中的第一个页码。页由 fileid:page 组合进行标识。
TAB
由于 ObjId 列已包含表的对象 ID,所以没有提供任何信息。
DB
由于 dbid 列已包含数据库的数据库 ID,所以没有提供任何信息。
在 sp_lock 的结果集内,锁定的 RID 资源类型中的资源描述为 1:1225:2。这表明在 fileid 1 内、页码为 1225 的页中、行标识符为 2 的行上应用了锁。
最关键的信息我认为是
[Database Id = 5 Object Id = 1254295528]和[Database Id = 5 Object Id = 484053356]
指明了加锁的两个对象。
select db_name(5) 结果为 cwbase_zclzj
连接 cwbase_zclzj帐套,
select object_name(484053356) 结果为 : erp_LS_BillCodeRule_GenerateNewBillCode
select object_name(1254295528) 结果为: erp_LS_BillFlowNoRecords_NewRecordSave
原来是两个存储过程搞得鬼,在生成单据编号的时候出现了死锁,因为两者是放在同一个事物中,也就验证了我们上面的猜想。简单的处理办法是不要放到同一个事物中,其他的办法就让负责维护的同事去管吧。
就写到这里,其实日志里面的大部分咚咚还是看不懂,等待高人给予解释。如果分析中有什么不对的,也请各位回复指出,我也进一步学习一下。谢谢!