Sql 当windows服务每隔几秒钟调用同一存储过程时,确保数据一致性

Sql 当windows服务每隔几秒钟调用同一存储过程时,确保数据一致性,sql,sql-server,stored-procedures,Sql,Sql Server,Stored Procedures,我们有一个存储过程,它返回需要处理的挂起项的列表。现在有了一个窗口服务,它每隔20秒调用一个存储过程,以获取挂起的项以供进一步处理 Pending表中有一列QueryTimestamp。对于待定项,QueryTimestamp列为空。存储过程选择后,QueryTimestamp列将用当前日期时间更新 正文如下。没有使用显式事务。SQL Server默认隔离级别适用 DECLARE @workerPending TABLE ( RowNum INT IDENTITY PRI

我们有一个存储过程,它返回需要处理的挂起项的列表。现在有了一个窗口服务,它每隔20秒调用一个存储过程,以获取挂起的项以供进一步处理

Pending
表中有一列
QueryTimestamp
。对于待定项,
QueryTimestamp
列为空。存储过程选择后,
QueryTimestamp
列将用当前日期时间更新

正文如下。没有使用显式事务。SQL Server默认隔离级别适用

DECLARE @workerPending TABLE
    (
        RowNum INT IDENTITY PRIMARY KEY,
        [PendingId] BIGINT,
        [CreatedDate] DATETIME
    )

INSERT INTO @workerPending ([PendingId], [CreatedDate])
     SELECT 
         [p].[PendingId] AS [PendingId],
         [p].CreatedDate
     FROM   
         [pending] [p] 
     WHERE          
         [p].QueryTimestamp IS NULL 
     ORDER BY
         [p].[PendingId]

--Update pending table with current date time
UPDATE Pnd  
SET QueryTimestamp = GETDATE()
FROM [Pending] Pnd
INNER JOIN @workerPending [wp] ON [wp].[PendingId] = Pnd.[PendingId]    
如果存储过程由于数据量过大而无法在20秒内处理第一个请求,windows服务将向该存储过程发送另一个调用,并开始处理这两个请求

问题是:这是否会导致两个请求都有一些重复的挂起记录

我们需要在挂起的表中实现锁吗

请建议我们如何确保数据一致性?因此,如果在前一个请求仍在进行时,另一个请求到达存储过程,则不应返回重复的记录


编辑:其他windows服务调用另一个SP,该SP将记录插入挂起的表中,并将“QueryTimestamp”标记为null。

简单但有效的解决方案是当服务要调用SP时,执行以下操作:

Read in table Settings a value that tells you if the thread is already running
If not already running then
begin
     Write to table Settings that the thread has started
     Commit this update
     Call your SP
     Write to table Settings that the thread has finished
     Commit this update
end

您可以使用子句在单个步骤中执行更新和选择。乙二醇

UPDATE pending  
SET QueryTimestamp = GETDATE()
output inserted.PendingId, inserted.CreatedDate
into @workerPending(PendingId,CreatedDate)
WHERE QueryTimestamp IS NULL
或者,允许您限制和排序结果并同时检索它们的更健壮的模式是在SELECT上使用事务和锁定提示,例如:

    begin transaction 

         INSERT INTO @workerPending ([PendingId], [CreatedDate])
         SELECT top 100
             [p].[PendingId] AS [PendingId],
             [p].CreatedDate
         FROM   
             [pending] [p] with (updlock, rowlock, readpast)
         WHERE          
             [p].QueryTimestamp IS NULL 
         ORDER BY
             [p].[PendingId];

        UPDATE pending 
        SET QueryTimestamp = GETDATE()
        where PendingIdin (select PendingId from @workerPending )

    commit transaction

请参见

您不需要插入子句或其上的顺序。您可以将该查询作为子查询包含在
更新中。或者创建一个自联接。这里的Pnd.[PendingId]=[wp].[PendingId]
子句是多余的,因为这也是连接条件。您可能应该重新考虑这个存储过程的编写方式实际上,这相当于[Pending]Pnd中QueryTimestamp为NULL的
UPDATE Pnd SET QueryTimestamp=GETDATE()。如果要存储修改后的行以备以后使用,可以使用
输出
条款。我已更新了查询,但我关心的是,是否确保在第一个请求未送达而第二个请求到达时不会返回重复数据?如果使用SQL Server代理或其他任务计划程序运行作业,只要第一个请求仍在运行,就不会有第二个请求。事务确保了隔离。更新是原子的,这意味着它将在单个事务中运行。在该事务期间,假设默认隔离级别为
readcommitted
,其他事务将无法访问任何受影响的行。我不太清楚您提到的内容。我无权对表格结构进行任何更改。如果不更改表格结构,则无法进行某些更改。如果不允许您这样做,则不允许您修复问题。不管是谁给你的,这都是愚蠢的。在这种情况下,您不需要更改表结构,您可以创建一个新表来保存一个标志,您可以使用该标志来知道SP已在运行。在我的回答中没有任何代码。这里只描述了您的服务应该如何工作。它告诉您服务在调用SP时应该使用的逻辑,因此在前一次调用未完成时,它将永远不会调用SP。是的,这不是链接显示的内容。您在这里没有将表视为队列。事实上,不能将表用作带有
UPDATE
语句的队列。所有
表作为队列
文章使用破坏性读取是有原因的,即
DELETE
with和OUTPUT子句来弹出条目。该事务将导致死锁,而不是更加健壮。您可以想象我是如何知道的…您可以将其作为单个更新…输出查询(如果您喜欢使用CTE),但它不会实质性地改变锁定行为。如果两个线程同时出现并且调用方未收到重复记录,它是否能保证数据一致性?我说的是700多张唱片。可以保证新的两个会话将获得相同的行。但是,保持表结构简单并测试不会出现死锁是很重要的。