7

MS Sql Server2008R2に単純なキューの実装があります。キューの本質は次のとおりです。

CREATE TABLE ToBeProcessed 
(
    Id BIGINT IDENTITY(1,1) PRIMARY KEY NOT NULL,
    [Priority] INT DEFAULT(100) NOT NULL,
    IsBeingProcessed BIT default (0) NOT NULL,
    SomeData nvarchar(MAX) NOT null
)

優先度とIsBeingProcessedがfalseであるIDの順に上位n行をアトミックに選択し、それらの行を更新して処理中であることを示します。Update、Top、Output、Order Byを組み合わせて使用​​すると思いましたが、残念ながら、Updateステートメントでtopとorderbyを使用することはできません。

そこで、更新を制限するin句を作成し、そのサブクエリが順序を実行します(以下を参照)。私の質問は、このステートメント全体がアトミックなのか、それともトランザクションでラップする必要があるのか​​ということです。

DECLARE @numberToProcess INT = 2

CREATE TABLE #IdsToProcess
(
    Id BIGINT NOT null
)

UPDATE 
    ToBeProcessed
SET
    ToBeProcessed.IsBeingProcessed = 1
OUTPUT 
    INSERTED.Id 
INTO
    #IdsToProcess   
WHERE
    ToBeProcessed.Id IN 
    (
        SELECT TOP(@numberToProcess) 
            ToBeProcessed.Id 
        FROM 
            ToBeProcessed 
        WHERE
            ToBeProcessed.IsBeingProcessed = 0
        ORDER BY 
            ToBeProcessed.Id, 
            ToBeProcessed.Priority DESC)

SELECT 
    *
FROM 
    #IdsToProcess

DROP TABLE #IdsToProcess

ダミー行を挿入するためのSQLを次に示します。

INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
4

2 に答える 2

8

質問の動機を理解している場合、2つの同時トランザクションが両方ともサブクエリを実行して上位N行を処理し、同じ行の更新に進む可能性を回避したいですか?

その場合、私はこのアプローチを使用します。

;WITH cte As
(
SELECT TOP(@numberToProcess) 
            *
        FROM 
            ToBeProcessed WITH(UPDLOCK,ROWLOCK,READPAST) 
        WHERE
            ToBeProcessed.IsBeingProcessed = 0
        ORDER BY 
            ToBeProcessed.Id, 
            ToBeProcessed.Priority DESC
)            
UPDATE 
    cte
SET
    IsBeingProcessed = 1
OUTPUT 
    INSERTED.Id 
INTO
    #IdsToProcess  

U以前、サブクエリを使用してバージョンを処理するときにSQL Serverがロックを取得し、2つの同時トランザクションが同じTOP N行を読み取るのをブロックするかどうかは少しわかりませんでした。これは当てはまらないようです。

テストテーブル

CREATE TABLE JobsToProcess
(
priority INT IDENTITY(1,1),
isprocessed BIT ,
number INT
)

INSERT INTO JobsToProcess
SELECT TOP (1000000) 0,0
FROM master..spt_values v1, master..spt_values v2

テストスクリプト(2つの同時SSMSセッションで実行)

BEGIN TRY
DECLARE @FinishedMessage VARBINARY (128) = CAST('TestFinished' AS  VARBINARY (128))
DECLARE @SynchMessage VARBINARY (128) = CAST('TestSynchronising' AS  VARBINARY (128))
SET CONTEXT_INFO @SynchMessage

DECLARE @OtherSpid int

WHILE(@OtherSpid IS NULL)
SELECT @OtherSpid=spid 
FROM sys.sysprocesses 
WHERE context_info=@SynchMessage and spid<>@@SPID

SELECT @OtherSpid


DECLARE @increment INT = @@spid
DECLARE @number INT = @increment

WHILE (@number = @increment AND NOT EXISTS(SELECT * FROM sys.sysprocesses WHERE context_info=@FinishedMessage))
UPDATE JobsToProcess 
SET @number=number +=@increment,isprocessed=1
WHERE priority = (SELECT TOP 1 priority 
                   FROM JobsToProcess 
                   WHERE isprocessed=0 
                   ORDER BY priority DESC)

SELECT * 
FROM JobsToProcess 
WHERE number not in (0,@OtherSpid,@@spid)
SET CONTEXT_INFO @FinishedMessage
END TRY
BEGIN CATCH
SET CONTEXT_INFO @FinishedMessage
SELECT ERROR_MESSAGE(), ERROR_NUMBER()
END CATCH

両方の同時トランザクションが同じ行を更新するため、ほぼ即座に実行が停止するため、ロックを取得する前に解放する必要があることSを識別しながら取得したロックは、2つのトランザクションが順番に行を取得してロックします。TOP 1 priorityUUX

ヒープ

CIが追加さALTER TABLE JobsToProcess ADD PRIMARY KEY CLUSTERED (priority)れると、代わりにデッドロックがほぼ即座に発生します。この場合、行Sロックは解放されず、1つのトランザクションが行のロックを取得してUロックへの変換を待機し、もう1つXのトランザクションはまだその行の変換を待機しています。SロックにロックしUます。

クラスター化されたインデックス

上記のクエリが変更されたMIN場合TOP

WHERE priority = (SELECT MIN(priority)
                   FROM JobsToProcess 
                   WHERE isprocessed=0 
                   )

次に、SQL Serverは、計画からサブクエリを完全に排除し、完全にUロックを取得します。

ここに画像の説明を入力してください

于 2011-04-08T18:43:29.617 に答える
2

個々のT-SQLステートメントはすべて、私の経験とこれまでに読んだすべてのドキュメントによると、アトミックであると想定されています。単一のT-SQLステートメントがあります。ergoはアトミックである必要があり、明示的なトランザクションステートメントは必要ありません。私はこの正確な種類のロジックを何度も使用しましたが、問題はありませんでした。支持できる代替意見として誰かがいるかどうかを見るのを楽しみにしています。

ちなみに、設定された数のアイテムを取得するには、ランキング関数、具体的にはrow_number()を調べてください。構文はおそらく少し厄介ですが、全体的には柔軟で強力なツールです。(それらを議論する約バジリオンのStack Overlowの質問と回答があります。)

于 2010-11-04T13:42:48.393 に答える