2017-06-13 27 views
0

我有一個包含數千個條目的數據庫表。我有多個工作線程一次接一行,做一些工作(每次大約需要一秒)。在拾取行時,每個線程都會更新數據庫行上的一個標誌(如時間戳),以便其他線程不會將其選中。但問題是,我最終在多個線程正在拾取同一行的情況下。從數據庫表上的多個線程查詢

我的一般問題是,我應該在這裏遵循什麼樣的通用設計方法來確保每個線程都獨立地選擇唯一的行並完成他們的任務。

注意:多個線程並行運行以加快數據庫行的處理速度。所以我想要儘可能小的關鍵部分或排他鎖。

只是爲了給出一些上下文,下面是存儲過程,它在更新行上的標誌後從表中拾取行。請注意,存儲過程不可編譯,因爲我已從中刪除了不必要的部分。但通常這就是它的結構。

多線程並行執行存儲過程時會發生問題。在一個線程中更新語句所做的更改(請注意更新在鎖定之後完成)對其他線程不可見,除非事務已提交。由於在UPDATE和TRANSACTION COMMIT之間有一條SELECT語句(大約需要50ms),在20%的情況下,線程中的UPDATE語句會選取一條已經被處理的行。

我希望我在這裏清楚。

USE ['mydatabase'] 
GO 

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 

ALTER PROCEDURE [dbo].[GetRequest] 
AS 
BEGIN 

    -- some variable declaration here 

    BEGIN TRANSACTION 

    -- check if there are blocking rows in the request table 
    -- FM: Remove records that don't qualify for operation. 

    -- delete operation on the table to remove rows we don't want to process 
    delete FROM request where somecondition = 1 


    -- Identify the requests to process 
    DECLARE @TmpTableVar table(TmpRequestId int NULL); 

    UPDATE TOP(1) request 
     WITH (ROWLOCK) 
      SET Lock = DateAdd(mi, 5, GETDATE()) 
     OUTPUT INSERTED.ID INTO @TmpTableVar 
      FROM request tur 
     WHERE (Lock IS NULL OR GETDATE() > Lock) -- not locked or lock expired 
      AND GETDATE() > NextRetry -- next in the queue 

     IF(@@RowCount = 0) 
     BEGIN 
      ROLLBACK TRANSACTION 
      RETURN 
     END 

    select @RequestID = TmpRequestId from @TmpTableVar 

    -- Get details about the request that has been just updated 
    SELECT  somerows 
    FROM  request 
    WHERE  somecondition = 1 

COMMIT TRANSACTION 
END 

enter image description here

+0

您可以發佈表'request'的列? –

+0

@IgorPaiva我更新了問題 – Dibzmania

回答

0

在SQL Server的一個關鍵部分的類似物是sp_getapplock,這是使用簡單。或者,您可以選擇要更新的行(UPDLOCK,READPAST,ROWLOCK)表提示。這兩個都需要一個多語句事務來控制獨佔鎖定的持續時間。

0

您需要在sql上啓動transaction isolation level以隔離您的線路,但這會影響您的性能。

看樣品:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

GO 

BEGIN TRANSACTION 

GO 

SELECT ID, NAME, FLAG FROM SAMPLE_TABLE WHERE FLAG=0 

GO 

UPDATE SAMPLE_TABLE SET FLAG=1 WHERE ID=1 

GO 

COMMIT TRANSACTION 

整理,不存在使用隔離級別更好的辦法。您需要分析每個級別隔離的正面和負面點並測試您的系統性能。

的更多信息:

https://docs.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql

http://www.besttechtools.com/articles/article/sql-server-isolation-levels-by-example

https://en.wikipedia.org/wiki/Isolation_(database_systems)

+0

中的列這不適用於我。如果我設置更高的隔離級別,那麼我基本上否定了擁有多個線程的優勢。我正在尋求儘可能低的顆粒級鎖定。 – Dibzmania