2012-07-05 31 views
2

我在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]實際上處於鎖定範圍內?

死鎖圖形: enter image description here

表模式(簡化): enter image description here

回答

1

它看起來像鎖是在不同的HOBT的。桌子上有多個索引嗎?

如果是這樣,則選擇with (updlock)可能只會對一個索引執行update鎖定。

+0

是的,有2個指標。你能暗示使用兩個索引嗎?我不熟悉HOBT的,所以也許你可以啓發我:) – Freek 2012-07-05 22:26:27

+0

霍布特是一個沒有索引(堆)或二叉樹(索引)的表。你不能暗示鎖定多個索引。 AaronBertrand的答案應該有所幫助,我們使用類似的東西。 – Andomar 2012-07-06 00:28:47

1

爲什麼不乾脆:

DECLARE @t TABLE(Id INT); 

UPDATE TOP (1) dbo.QueuedImportJobs 
    SET LeaseTimeout = DATEADD(MINUTE, 5, CURRENT_TIMESTAMP) 
    OUTPUT inserted.Id INTO @t 
    WHERE Status = @Status 
    AND COALESCE(LeaseTimeout, '19000101') < CURRENT_TIMESTAMP; 

SELECT <cols> FROM dbo.QueuedImportJobs 
    WHERE Id IN (SELECT Id FROM @t); 

順便說一句,你可能希望有ORDER BY確保所選行是根據所需的索引順序隊列中的第一個。如果Id上的索引聚集在一起,這可能是它的工作方式,但除非你這麼說,否則沒有保證。這將需要查詢的輕微的重新構建,因爲你不能在一個UPDATE申請ORDER BY(或索引提示)直接,例如:

WITH x AS 
(
    SELECT TOP (1) Id, LeaseTimeout 
    FROM dbo.QueuedImportJobs 
    WHERE Status = @Status 
    AND COALESCE(LeaseTimeout, '19000101') < CURRENT_TIMESTAMP 
    ORDER BY Id 
) 
UPDATE x 
    SET LeaseTimeout = DATEADD(MINUTE, 5, CURRENT_TIMESTAMP) 
    OUTPUT inserted.id INTO @t; 
+0

使用此查詢,OP將如何檢索聲明的作業的詳細信息?輸出子句可能有幫助 – Andomar 2012-07-05 21:20:14

+0

@Andomar請再次檢查 – 2012-07-05 21:23:21

+0

這可能確實有效,明天我會檢查出來。仍然奇怪爲什麼我的解決方案不起作用。 – Freek 2012-07-05 22:25:46