【发布时间】:2012-07-05 21:12:57
【问题描述】:
我正在 SQL Server 中实现一个队列(请不要对此进行讨论)并且遇到了竞争条件问题。感兴趣的 T-SQL 如下:
set transaction isolation level serializable
begin tran
declare @RecordId int
declare @CurrentTS datetime2
set @CurrentTS=CURRENT_TIMESTAMP
select top 1 @RecordId=Id from QueuedImportJobs with (updlock) where Status=@Status and (LeaseTimeout is null or @CurrentTS>LeaseTimeout) order by Id asc
if @@ROWCOUNT> 0
begin
update QueuedImportJobs set LeaseTimeout = DATEADD(mi,5,@CurrentTS), LeaseTicket=newid() where Id=@RecordId
select * from QueuedImportJobs where Id = @RecordId
end
commit tran
RecordId 是 PK,Status,LeaseTimeout 上也有索引。
我所做的基本上是选择一条租约恰好到期的记录,同时将租约时间更新为 5 分钟并设置新的租约票证。
所以问题是,当我使用几个线程并行运行此代码时,我遇到了死锁。我已经对其进行了调试,直到发现更新语句有时会为同一条记录执行两次。现在,我的印象是with (updlock) 应该防止这种情况发生(顺便说一句,xlock 也会发生这种情况,而不是tablockx)。所以看起来实际上在同一范围的记录上有一个 RangeS-U 和一个 RangeX-X 锁,这应该是不可能的。
那么我错过了什么?我认为这可能与前 1 子句有关,或者 SQL Server 不知道 where Id=@RecordId 实际上在锁定范围内?
死锁图:
表架构(简化):
【问题讨论】:
标签: sql-server tsql locking deadlock