2010-01-21 46 views
4

說我有如下表:選擇並更新表,所以沒有線程的重疊

ID|Read 
------- 
1|true 
2|false 
3|false 
4|false 

...我需要閱讀的最小ID,有[閱讀] ==虛假;另外,更新我現在已經閱讀它。

因此,如果我執行我的存儲過程dbo.getMinID,它將返回ID:2,並更新[Read] - > true。

CREATE PROCEDURE [dbo].[getMinID] 
(
    @QueryID INT OUTPUT 
) 
BEGIN 
    SELECT TOP 1 @QueryID = [ID] from Table 
    UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 
END 

的問題是,我有十(10)異步線程執行dbo.getMinID,在同一時間,我不能有選擇它們在任何情況下相同的[ID]。我擔心我的SELECT和UPDATE語句之間執行第二個線程,因此在兩種情況下返回[ID]:2。

無論有多少線程在存儲過程中執行操作,我如何確保不會選擇/更新同一個記錄兩次?另外,請記住,表CONSTANTLY添加了新的行,所以我不能鎖定表!

回答

3

如果您的意思是併發安全隊列類型鎖定,那麼使用ROWLOCK,UPDLOCK,READPAST提示?

SQL Server Process Queue Race Condition

BEGIN TRAN 

SELECT TOP 1 @QueryID = [ID] from Table WITH (ROWLOCK, UPDLOCK, READPAST) 
UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 

COMMIT TRAN -- TRAM 

然而,在一個聲明。像

WITH T AS 
(
    --ORDER BY with TOP , or perhaps MIN is better? 
    SELECT TOP 1 [Read], [ID] from Table 
    WITH (ROWLOCK, UPDLOCK, READPAST) ORDER BY [Read] 
) 
UPDATE 
    T 
SET 
    [Read] = 1; 
+0

min,不會觸發所有行上的鎖?或者我只是說廢話 – 2010-01-21 13:11:33

+0

共享鎖是隻讀的,並且由於READPAST它將跳過其他鎖。沒關係,真的... – gbn 2010-01-21 13:52:21

0

將select和update以及select語句放入事務中並在事務啓動時鎖定表,以使其他線程等待。 最好的問候, 約爾丹

+0

我不想鎖定表,因爲插入是不斷的。再加上明天可能會有1000個線程在同一個表上運行,這意味着我需要一個不太乾擾,但明確的方式來做到這一點。 – 2010-01-21 12:45:33

+1

@Theofanis - 沒關係*如果你搞亂了數據,你的db訪問速度如何?首先以最簡單的方式嘗試它(通過交易),然後*挑戰績效。 – 2010-01-21 12:50:15

+1

如果是這種情況,並且線程的數量可能會增加到1000,則嘗試在表中添加一個新的空列。第一件事情就是將它的id寫在新列中,所以沒有其他線程可以佔用這一行 – IordanTanev 2010-01-21 12:52:30

1

如果你希望它是原子,你必須鎖定的東西,但是,這並不意味着你必須將其鎖定長期。我會先用一些限定範圍的交易嘗試,但我也很想嘗試更新的變種,在同一時間做了SELECT

UPDATE TOP (1) [foo] 
SET [read] = 1 
OUTPUT INSERTED.id 
WHERE [read] = 0 

你可以看到如果有任何併發​​問題 - 誠實地說,我不知道沒有檢查!您可能需要添加諸如WITH (ROWLOCK)之類的內容。但個人而言,我想保持簡單並嘗試一個可序列化的事務。

另外請注意,這並不能保證其記錄你會得到

+0

我不介意鎖定ROW,只要我不鎖定表。 – 2010-01-21 12:52:52

+0

你的意思是將事務隔離級別設置爲可序列化? – 2010-01-21 12:53:32

+0

如果將事務級別設置爲可序列化並使用事務,則應該能夠使用*原始*代碼。 – 2010-01-21 12:56:11

1

讓你的事務隔離級別SERIALIZABLE並放置一個排它鎖與SELECT命令(倒數第一?):

SELECT TOP 1 @QueryID = [ID] from Table WITH (XLOCK) ORDER BY id DESC 
UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 

這將在最高密鑰範圍內放置一個XLOCK,並阻止併發查詢讀取最高記錄。

這樣,沒有交易會獲得相同的記錄。

相關問題