我在SQL Server中實現了一個隊列(請不要討論這個問題),並且遇到爭用條件問題。感興趣的T-SQL如下:帶範圍鎖定的SQL Server爭用條件問題
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 [email protected] 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 [email protected]
select * from QueuedImportJobs where Id = @RecordId
end
commit tran
RecordId
是PK和也有上Status,LeaseTimeout
的索引。
我基本上在做的是選擇一個租約恰好到期的記錄,同時用5分鐘更新租約時間並設置一個新的租賃票。
所以問題是當我使用幾個線程並行運行這段代碼時,我遇到了死鎖。我已經對它進行了調試,直到我發現update語句有時會爲同一條記錄執行兩次。現在,我的印象是with (updlock)
應該防止這種情況(它也發生在xlock
btw,而不是tablockx
)。所以它實際上看起來像是在同一範圍的記錄上存在RangeS-U和RangeX-X鎖定,這應該是不可能的。
那麼我錯過了什麼?我認爲這可能與頂端1子句有關,或者SQL Server不知道where [email protected]
實際上處於鎖定範圍內?
死鎖圖形:
表模式(簡化):
是的,有2個指標。你能暗示使用兩個索引嗎?我不熟悉HOBT的,所以也許你可以啓發我:) – Freek 2012-07-05 22:26:27
霍布特是一個沒有索引(堆)或二叉樹(索引)的表。你不能暗示鎖定多個索引。 AaronBertrand的答案應該有所幫助,我們使用類似的東西。 – Andomar 2012-07-06 00:28:47