在 SQL 2008+ 中,以下 Queue table 和 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 锁来有效地锁定在生成器写入命名队列之前没有可用行的消费者?
创建索引 [IDX_Queue_IsFailed_QueueName_DelayUntilUTC] ON [dbo].[Queue] ([IsFailed], [QueueName], [DelayUntilUTC])
另外,我想 OrderBy 无关紧要,只要我们总是得到一个没有前任的。