我有一个系统,中央MSSQL数据库在表中保存需要完成的作业队列。
由于处理要求不会那么高,并且请求频率不是特别高(可能每隔几秒钟一次),我们决定让使用队列的应用程序只是查询数据库每当需要时;目前没有消息队列服务。
通过让客户端应用程序运行存储过程来执行单次提取,该存储过程执行所涉及的查询并返回作业ID。然后,客户端应用程序通过按ID查询来获取作业信息,并将作业设置为已处理。
表现很好;我们感受到的唯一障碍是,因为客户端应用程序必须查询详细信息并在作业被标记为已处理之前执行检查,在非常罕见的情况下(每几千个工作一次),两个客户端选择相同的工作
作为一种解决这个问题的方法,我建议让初始存储过程运行“标记”它随时间和日期提取的记录。查询记录时,存储过程将仅拉出此“标记”过去一定时间(例如5秒)的记录。这样,如果存储过程在5秒内运行两次,则第二个实例将不会获取相同的作业。
任何人都可以预见到以这种方式解决问题或提供替代解决方案的任何问题吗?
答案 0 :(得分:0)
使用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