2011-06-17 105 views
3

我需要建立一個使用一些SQL表的隊列系統,如here所述。這之中,因爲我需要過濾的不同性判據排隊的項目,在存儲過程中,我使用ORDER BY AND WITH(ROWLOCK,UPDLOCK,READPAST)

BEGIN TRANSACTION 

CREATE TABLE #Temp (ID INT, SOMEFIELD INT) 

INSERT INTO #Temp SELECT TOP (@Something) TableA.ID, TableB.SomeField FROM TableA WITH (ROWLOCK, READPAST) INNER JOIN TableB WITH (UPDLOCK, READPAST) WHERE Condition1 

INSERT INTO #Temp SELECT TOP (@Something) TableA.ID, TableB.SomeField FROM TableA WITH (ROWLOCK, READPAST) INNER JOIN TableB WITH (UPDLOCK, READPAST) WHERE Condition2 

(...) 

UPDATE TableB SET SomeField = 1 FROM TableB WITH (ROWLOCK, READPAST) WHERE ID IN (SELECT ID FROM #Temp) 

COMMIT TRANSACTION 

我在第二使用的第一臺ROWLOCKUPDLOCK,因爲在此之後選擇,我將僅更新TableB,但我需要確保這些行不會被任何其他併發查詢在TableA中更新。一切都很順利,直到我需要在上面的任何一個SELECT s中插入ORDER BY子句爲止,這樣只有特定的ID纔會被選中(我必須真的這樣做)。發生什麼是:

1)沒有ORDER BY,兩個併發執行按需要執行,返回不同且不重疊的結果;但是,他們不會返回我想要的結果,因爲那些準確的結果超出了每個SELECT聲明的範圍。

2)使用ORDER BY和兩個併發執行,只有第一個返回結果。第二個不返回任何東西。

我記得在博客上看到,對於使用WITH (ROWLOCK, READPAST)ORDER BY工作的這類查詢,需要在排序中使用的字段上創建索引。我試過了,但我得到了同樣的結果。我怎樣才能克服這個問題?

編輯:舉例來說,如果我有一個表TestTable與字段(TestID INT,價值INT)和值 「(1,1),(2,2),...」,並執行 「同步」

BEGIN TRANSACTION 

SELECT TOP 2 TestID FROM TestTable WITH (UPDLOCK, READPAST) 

WAITFOR DELAY '00:00:05' 

COMMIT TRANSACTION 

第一次執行返回行(1,2),第二次執行返回行(3,4)。但是,如果我執行

BEGIN TRANSACTION 

SELECT TOP 2 TestID FROM TestTable WITH (UPDLOCK, READPAST) ORDER BY VALUE ASC 

WAITFOR DELAY '00:00:05' 

COMMIT TRANSACTION 

第一個返回(1,2),第二個不返回任何內容。爲什麼是這樣?!

回答

4

正如預期的那樣

  • 的SELECT與ORDER BY,沒有ROWLOCK,沒有指數將有一個表鎖,因爲掃描/中間排序的制定TOP 2.因此,第二屆跳過整個table因爲READPAST

  • 沒有ORDER BY的SELECT只是選取任何2行,碰巧按插入的順序(純巧合,沒有默示順序)。這兩行被鎖定的事實導致第二個會話跳到下一個非鎖定的行。

SQL Server嘗試保持鎖儘可能粒度,但掃描意味着表鎖。現在,這通常不會有所作爲(它會是一個共享讀鎖),但你有UPDLOCK也意味着一個獨佔鎖定表

所以,你需要這兩個

  • 3的提示在SELECT查詢(ROWLOCK,UPDLOCK,READPAST)中控制粒度,隔離和併發性。
    僅使用ROWLOCK仍然會導致對每一行進行獨佔鎖定以進行掃描/排序。
  • Value INCLUDE TestID上的索引使SELECT有效。索引只能解決併發問題,但不能保證。

在你我掛我的回答SQL Server Process Queue Race Condition在那裏我有所有3鎖定前面的問題(在評論)一個提示