SQL 2008+ では、次の Queue テーブルと Enqueue、Dequeue 操作は、複数のジェネレーターとコンシューマー間のストリーム内で効率的なジョブ キューイングを任意の名前付きキューで任意の部分的に順序付けられた連続で許可することを目的としています。RetryLater()、FailNow()、Reset() (表示されていません) によるポイズン メッセージ処理の簡単なサポート。
CREATE TABLE [dbo].[Queue](
[ID] BIGINT IDENTITY(1,1) NOT NULL CONSTRAINT [PK_Queue] PRIMARY KEY,
[PredecessorID] BIGINT NULL,
[QueueName] NVARCHAR(255) NOT NULL,
[DataType] NVARCHAR(255) NOT NULL,
[Data] NVARCHAR(MAX) NOT NULL,
[RetriesRemaining] INT CONSTRAINT [CHK_RetryCount] CHECK ([RetriesRemaining] IS NULL OR 0 <= [RetriesRemaining]),
[IsFailed] AS (CASE WHEN [RetriesRemaining] IS NULL OR 0 < [RetriesRemaining] THEN 0 ELSE 1 END) PERSISTED,
[QueuedOnUTC] DATETIME NOT NULL DEFAULT (GETUTCDATE()),
[DelayUntilUTC] DATETIME NULL,
[LastFailedOnUTC] DATETIME NULL,
[LastFailure] NVARCHAR(MAX)
)
-- Enqueue
INSERT INTO [dbo].[Queue]
(
[PredecessorID],
[QueueName],
[DataType],
[Data],
[RetriesRemaining]
)
VALUES
(
@PredecessorID,
@QueueName,
@DataType,
@Data,
@RetriesRemaining
)
-- Dequeue
SELECT
[Dequeued].[ID],
[Dequeued].[QueueName],
[Dequeued].[DataType],
[Dequeued].[Data]
FROM
[dbo].[Queue] AS [Dequeued] WITH (ROWLOCK, UPDLOCK, READPAST)
LEFT JOIN
[dbo].[Queue] AS [Predecessor] ON
[Dequeued].[PredecessorID] = [Predecessor].[ID]
WHERE
[Dequeued].[IsFailed] = 0
AND [Dequeued].[QueueName] = @QueueName
AND ([Dequeued].[DelayUntilUTC] IS NULL OR [Dequeued].[DelayUntilUTC] < GETUTCDATE())
AND [Predecessor].[ID] IS NULL
コンシューマ トランザクションは、Dequeue() の前からアプリケーション側の処理を通じて、DELETE、RetryLater()、または FailNow() まで開いたままになります。
- この設計は健全ですか?
- このセットアップのデッドロックはありませんか?
- IDX_Queue_IsFailed_QueueName_DelayUntilUTC またはその他のインデックスがデッドロックフリーネスに与える可能性のある悪影響は何ですか?
- 他に有益な指標は何ですか?
- テーブルのパーティショニング (queueName による) やその他の機能によって、スケーラビリティがどのように向上しますか?
考えられるように、ジェネレーターは、タイトなポーリングを回避するためにデータがキューに入れられたことを外部メカニズムを通じてコンシューマーに通知します。代わりに (SQL ServiceBroker を使用せずに) SQL Locks を使用して、ジェネレーターが名前付きキューに書き込むまで利用可能な行がない Consumer を効率的にロックする方法はありますか?
CREATE INDEX [IDX_Queue_IsFailed_QueueName_DelayUntilUTC] ON [dbo].[Queue] ([IsFailed], [QueueName], [DelayUntilUTC])
また、先行するものがないものを常に取得する限り、OrderBy は問題ではないと思います。