数据库轮询,防止重复回迁

Database polling, prevent duplicate fetches

本文关键字:数据库      更新时间:2023-10-16

我有一个系统,中央MSSQL数据库将需要完成的作业队列保存在表中。

由于处理要求不会那么高,请求频率也不会特别高(可能最多几秒钟一次),我们决定让使用队列的应用程序在需要时简单地查询数据库;此时没有消息队列服务。

通过让客户端应用程序运行存储过程来执行单个获取,该存储过程执行所涉及的查询并返回作业ID。然后,客户端应用程序通过按ID查询来获取作业信息,并将作业设置为已处理。

性能良好;我们感觉到的唯一障碍是,因为客户端应用程序必须在作业标记为已处理之前查询详细信息并执行检查,所以在极少数情况下(每几千个作业一次),两个客户端会选择同一个作业。

作为解决这个问题的一种方法,我建议让运行的初始存储过程用时间和日期"标记"它提取的记录。存储过程在查询记录时,只会提取该"标记"为过去某一时间量(例如5秒)的记录。这样,如果存储过程在5秒内运行两次,则第二个实例将不会获得相同的作业。

有人能预见到以这种方式解决问题会有什么问题吗?或者能提供一个替代解决方案吗?

使用UNIQUEIDENTIFIER字段作为标记。当存储过程运行时,锁定正在读取的行,并使用NEWID()更新字段。如果您担心死锁问题,可以使用WITH(READPAST)之类的东西标记轮询语句。

在这里使用GUID的原因是有一个唯一的标识符,用于标记批次。您的NEWID()调用保证为您提供一个唯一的值,该值将用于防止您意外地两次获取相同的数据。GETDATE()在这里不起作用,因为最终可能会有两个调用同时解析;BIT不起作用,因为它不会唯一地标记用于提取或报告的批次。

例如,

declare @ReadID uniqueidentifier
declare @BatchSize int = 20; -- make a parameter to your procedure 
set @ReadID = NEWID();
UPDATE tbl WITH (ROWLOCK)  
SET HasBeenRead = @ReadID -- your UNIQUEIDENTIFIER field
FROM (
  SELECT  TOP (@BatchSize)  Id  
  FROM  tbl WITH(UPDLOCK ROWLOCK READPAST )
  WHERE HasBeenRead IS null ORDER BY [Id]) 
AS t1 
WHERE ( tbl.Id = t1.Id)
SELECT Id, OtherCol, OtherCol2 
FROM tbl WITH(UPDLOCK ROWLOCK READPAST ) 
WHERE HasBeenRead = @ReadID 

然后你可以使用像这样的轮询语句

SELECT COUNT(*) FROM tbl WITH(READPAST) WHERE HasBeenRead IS NULL

改编自此处:https://msdn.microsoft.com/en-us/library/cc507804%28v=bts.10%29.aspx