代码逻辑死锁导致数据库超时

又是很久没记录工作点滴了,年后就忙着找新东家了,总算尘埃落定。不料,刚交接出去的系统就出问题了,系统老是超时。两天前看测试环境报错还没在意,因为什么代码也没改而且正式系统上也没问题,再加上公司权限系统切换有问题,大家什么异常第一个就想到权限系统。今天发现正式库上也报错了,一直报超时。于是查问题,最终结果是交接的人新加了个功能,代码中人为照成锁等待超时(在函数中前面加事务,后面没加事务,后面没加事务的等待锁释放,而事务等待执行完提交,于是造成等待超时)。

错误抽象出来如下:

public static void Test()
{
    using (SqlConnection myConnection = DBHelper.MyConnection)
    {
        myConnection.Open();
        SqlTransaction _varTrans = myConnection.BeginTransaction();
        try
        {
            InsertA(_varTrans);
            UpdateA(_varTrans);
            SelectA();
            _varTrans.Commit();
        }
        catch (Exception ex)
        {
            _varTrans.Rollback();
            Console.WriteLine(ex.ToString());
        }
    }
}
运行症状,每次执行这个方法都会卡在SelectA那里,直到数据库超时、报异常、释放锁。原因是因为在插入和更新时该事务会把表锁住不让其他进程进来访问,这时SelectA也就访问不了这个表(因为它不属于那个事务,不是锁的拥有者),只能等待那个事务提交然后释放锁,然而只要SelectA不执行,那个事务就提交不了,照成逻辑上的死锁(不是数据库的死锁,因为等待到超时时就会报异常,自然解除锁表)。所以每当执行这个方法时,所有操作这个表的程序都会报错(我们程序中,对于操作失败会每隔三分钟再去尝试,所以就导致这个表几乎废了,还好有缓存机制,否则全线瘫痪)。
查询方法:每次一卡,首先看所有的锁:exec sp_lock,查看ObjId不为0的,根据dbcc inputbuffer(spid)--spid为所有锁中的spid字段,看到执行的SQL语句为EventInfo字段,找到相应的代码。到代码中一找,原来刚接手项目的哥们新加了一个功能,断点调试,发现运行到后面的无事务查询卡住了,导致逻辑死锁,卡住的这段时间到数据库执行查询也是超时,遂以为这就是数据库的死锁。查看相关的资料发现,这种只是逻辑等待超时。

问题是解决了,然后就想可不可以用with(nolock)或者是with(readpast)的,经过业务考虑不适合;
进一步思考哪些情况会导致锁表等待,经过调查,个人从表象认为插入操作时锁主键索引,更新时锁定行,删除时锁主键索引。(具体测试代码见源码
查询数据库锁表的语句如下:

--查看所有锁表的语句
use master;
go
if not exists(select * from sysobjects where name = '#LockDetails' and xtype='U')
	begin
	CREATE Table #LockDetails
	(
	[EventType] varchar(2000),
	[Parameters] int,
	EventInfo nvarchar(2000)
	);
	end
go
DECLARE crsr Cursor FOR
SELECT spid blk FROM (
	select distinct resource_database_id dbid,
	resource_associated_entity_id objid,
	request_mode lockmode,
	request_session_id spid,
	b.name from master.sys.dm_tran_locks a
	left join  master.sys.sysdatabases b on a.resource_database_id=b.dbid
	where resource_associated_entity_id<>0 
) a;
DECLARE @blk int;
--游标开始
open crsr;
FETCH NEXT FROM crsr INTO @blk;
WHILE (@@FETCH_STATUS=0)
BEGIN;
	INSERT INTO #LockDetails exec('dbcc inputbuffer('+@blk+')')
	FETCH NEXT FROM crsr INTO @blk;
END;
close crsr;
DEALLOCATE crsr;
--游标结束
select distinct * from #LockDetails;
DROP Table #LockDetails;

源码下载

参考:

SQL Server死锁总结 :http://www.cnblogs.com/happyhippy/archive/2008/11/14/1333922.html
msdn的sp_lock :http://msdn.microsoft.com/zh-cn/library/ms187749.aspx
msdn的sys.dm_tran_locks :http://msdn.microsoft.com/zh-cn/library/ms190345.aspx
msdn的DBCC INPUTBUFFER:http://msdn.microsoft.com/zh-cn/library/ms187730(v=SQL.105).aspx

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 SQL Server 中遇到死锁情况时,可以尝试以下方法来解决: 1. 监控死锁:使用 SQL Server Profiler 或 Extended Events 来监视死锁事件,并分析死锁图形以了解导致死锁的具体原因。 2. 优化查询:检查查询语句和事务,确保它们使用合适的索引和适当的事务隔离级别。通过优化查询,可以减少死锁可能性。 3. 设置合适的事务隔离级别:根据业务需求,选择适当的事务隔离级别。较低的隔离级别(如读已提交)可以减少死锁可能性,但也可能导致脏读。 4. 调整锁超时时间:根据系统的具体情况,可以适当增加锁超时时间,以减少死锁发生的机会。 5. 使用锁提示(Lock Hints):通过在查询语句中使用锁提示,如行级锁(ROWLOCK),可以限制锁的范围,减少死锁可能性。但要注意,在使用锁提示时需谨慎,避免引入其他性能问题。 6. 重构代码和事务逻辑:重新审视代码和事务逻辑,确保使用最小的事务范围,并尽量减少长时间持有锁的情况。 7. 使用数据库引擎的自动死锁检测和解决机制:SQL Server 中提供了自动检测和解决死锁的机制,如死锁图形检测和锁超时处理。可以根据具体情况考虑是否启用这些机制。 8. 升级数据库版本:如果使用的是较旧的 SQL Server 版本,考虑升级到最新版本,以获得更好的死锁处理性能和功能。 请注意,在尝试解决死锁问题之前,建议先备份数据库,并在生产环境之外进行测试。此外,对于复杂的死锁问题,也可以考虑寻求专业的数据库管理员(DBA)或 Microsoft 支持的帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值