web-dev-qa-db-ja.com

SQLServerプロセスキューの競合状態

ストアドプロシージャを介して複数の注文プロセッサからアクセスされる注文キューがあります。各プロセッサは一意のIDを渡します。このIDは、次の20件の注文を自分で使用するためにロックするために使用されます。次に、ストアドプロシージャは、これらのレコードを注文プロセッサに返し、処理を行います。

複数のプロセッサが同じ「OrderTable」レコードを取得できる場合で同時に処理しようとする場合があります。これにより、最終的にはプロセスの後半でエラーがスローされます。

私の次の行動方針は、各プロセッサが利用可能なすべての注文を取得し、プロセッサをラウンドロビンすることですが、コードスレッドのこのセクションを安全にし、プロセッサがいつでもレコードを取得できるようにしたいと考えていました。

明示的に-この競合状態が発生している理由と、問題を解決する方法についてのアイデア。

BEGIN TRAN
    UPDATE  OrderTable WITH ( ROWLOCK )
    SET     ProcessorID = @PROCID
    WHERE   OrderID IN ( SELECT TOP ( 20 )
                                        OrderID
                                FROM    OrderTable WITH ( ROWLOCK )
                                WHERE   ProcessorID = 0)
COMMIT TRAN


SELECT  OrderID, ProcessorID, etc...
FROM    OrderTable
WHERE   ProcessorID = @PROCID
44

編集:

私は自分の答えをチェックするためにグーグルで検索しました "SQL ServerでのREADPASTおよびUPDLOCKを使用したデータキューの処理" 。私がこのソリューションを読んで遊んだのは何年も前のことです。

元の:

READPASTヒントを使用する場合、ロックされた行はスキップされます。 ROWLOCKを使用したので、ロックのエスカレーションを回避する必要があります。私が知ったように、UPDLOCKも必要です。

したがって、プロセス1は20行をロックし、プロセス2は次の20行を取得し、プロセス3は行41から60を取得します。

更新は次のように書くこともできます:

UPDATE TOP (20)
    foo
SET
    ProcessorID = @PROCID
FROM
    OrderTable foo WITH (ROWLOCK, READPAST, UPDLOCK)
WHERE
    ProcessorID = 0

更新、2011年10月

これは、SELECTとUPDATEが一度に必要な場合に、OUTPUT句を使用してよりエレガントに行うことができます。

56
gbn

ServiceBrokerを使用できます。また、sp_getapplockを使用して、行へのアクセスをシリアル化することもできます。これにより、競合状態が解消されます。

「独自のロック(SQLのミューテックス)を作成して並行性を支援する」 http://sqlblogcasts.com/blogs/tonyrogerson/archive/2006/06/30/855.aspx

6
A-K