SQL Server 2005游标的行为变化导致的死锁问题
产生死锁的场景:
连接1:
DECLARE @cursor INT
EXEC sp_cursoropen @cursor OUTPUT, 'select * from employee', 2, 2
EXEC sp_cursorfetch @cursor, 2, 0,1
select @cursor
--返回180150003
连接2:
DECLARE @cursor INT
EXEC sp_cursoropen @cursor OUTPUT, 'select * from employee', 2, 2
EXEC sp_cursorfetch @cursor, 2, 0,1
select @cursor
现在连接2被连接1阻塞
接下来执行:
连接1:
EXEC sp_cursor 180150003, 33, 1, '', @ContactID=5 –这个语句实际上是做update
出现错误1205。
连接2仍旧被连接1阻塞:
连接1正持有键上的U锁,连接2在等待同一个键值上的U锁。
我们来具体看一下游标的几个调用方式在数据库上申请和释放lock的过程
--创建一个dynamic游标
DECLARE @cursor INT
EXEC sp_cursoropen @cursor OUTPUT, 'select * from employee', 2, 2
--提取下一行;这将行放到fetch buffer中
EXEC sp_cursorfetch @cursor, 2, 0,1
--更新fetch buffer中的行
EXEC sp_cursor @cursor, 33, 1, '', @zip=5
--关闭游标
EXEC sp_cursorclose @cursor
这里我们介绍trace flag 1200来输出语句在数据库上对锁申请和释放的日志:
DBCC traceon(1200,-1)
关闭trace flag: dbcc traceoff(1200,-1)
对于上面的语句,在2005版本中:
EXEC sp_cursorfetch @cursor, 2, 0,1
Process 53 acquiring S lock on RID: 6:1:60795:0 (class bit0 ref1) result: OK
Process 53 acquiring U lock on RID: 6:1:60795:0 (class bit10000000 ref0) result: OK
EXEC sp_cursor @cursor, 33, 1, '', @zip=5
Process 53 acquiring U lock on RID: 6:1:60795:0 (class bit0 ref1) result: OK
Process 53 acquiring X lock on RID: 6:1:60795:0 (class bit2000000 ref0) result: OK
Process 53 releasing lock reference on RID: 6:1:60795:0
在2000版本:
EXEC sp_cursoropen @cursor OUTPUT, 'select * from employee', 2, 2
Process 51 acquiring U lock on RID: 5:1:240:0 (class bit2000000 ref1) result: OK
Process 51 acquiring U lock on RID: 5:1:240:0 (class bit10000000 ref0) result: OK
Process 51 acquiring U lock on RID: 5:1:240:0 (class bit0 ref1) result: OK
EXEC sp_cursor @cursor, 33, 1, '', @zip=5
Process 51 acquiring U lock on RID: 5:1:240:0 (class bit0 ref1) result: OK
Process 51 acquiring X lock on RID: 5:1:240:0 (class bit2000000 ref1) result: OK
Process 51 releasing lock reference on RID: 5:1:240:0
接下来,我们对上面现象进行一定的说明:
在2005上,从cursor打开,到执行update,整个lock的变化过程为:
S(共享锁)à U(更新锁)-->X(排他锁)
Process 53 acquiring S lock on RID: 6:1:60795:0 (class bit0 ref1) result: OK
Process 53 acquiring U lock on RID: 6:1:60795:0 (class bit0 ref1) result: OK
Process 53 acquiring X lock on RID: 6:1:60795:0 (class bit2000000 ref0) result: OK
而在2000上,从cursor打开,到执行update,整个lock的变化过程为:
Process 51 acquiring U lock on RID: 5:1:240:0 (class bit10000000 ref0) result: OK
Process 51 acquiring U lock on RID: 5:1:240:0 (class bit0 ref1) result: OK
Process 51 acquiring X lock on RID: 5:1:240:0 (class bit2000000 ref1) result: OK
1.在该错误重现中,滚动锁定(scroll lock)在GetRow上提取S锁,而后是U锁,就导致了死锁和错误结果的出现。
2.提取并没有滚动锁定基本的索引
正如上面那个简单的错误重现,我们可以发现,2005中 fetch过程中S锁到U锁的升级导致了deadlock的出现。对于S锁,两个连接可以在同一时间申请同一行数据上的S锁,而当两个连接都有对S锁进行升级到U锁的时候,一定会出现deadlock。
在SQL Server 2000版本中,fetch锁定直接请求U锁,这种情况下,就只有一个连接可以在同一时间对同一数据行上申请U锁了。这样直接申请U的行为,虽然避免了deadlock,但是会导致更多的lock和更长的lock持有时间,从而导致更多的阻塞出现。
这实际上是SQL Server 2005的一个设计变化,SQL Server2008也是如此。除了使用相应的参数保证两个连接不要访问同样的数据,没有解决该问题的方案。
Comments
- Anonymous
October 17, 2011
hi,最近遇到一个死锁,希望能得到解释,谢谢。www.cnblogs.com/.../2192006.html