不断检查 table 记录并处理它们的存储过程

Stored procedure to constantly check table for records and process them

我们有一个进程导致脏读错误。所以,我正在考虑将其重新设计为一个队列,其中有一个进程通过队列。

我的想法是创建一个 table 可以插入各种进程。然后,一个进程实际处理table中的记录。但是,我们需要处理方法的实时结果。因此,虽然我正在考虑每隔几秒将它安排到 运行,但这可能不够快。我不希望用户等待几秒钟的结果。

基本上,我在考虑使用无限循环,以便一个存储过程不断 运行ning,并且该存储过程创建事务来执行更新。

可能是这样的:

WHILE 1=1
BEGIN
    --Check for new records
    IF NewRecordsExist
    BEGIN
        --Mark new records as "in process"
        BEGIN TRANSACTION

            --Process records
            --If errors, Rollback
            --Otherwise Commit transaction
    END
END

但是,我不希望 SQL 服务器因这种方法而负担过重。基本上,我希望这个 运行ning 一直在后台运行,而不是耗尽处理器能力。除非它有很多事情要做。然后,我希望它完成它的工作。有更好的设计模式吗?具有无限循环的存储过程是线程安全的吗?我一直在 Windows 进程中使用此模式,但此特定任务不适合 Windows 进程。

更新:我们设置了事务锁定。这不是问题。我们正在尝试预留订单商品。因此,我们有一个存储过程启动一个事务,检查可用的内容,然后更新 table 以标记保留的内容。

问题在于,当两个不同的用户同时尝试预订同一产品时,第一个过程会检查可用性,找到可用的产品,然后开始预订。但是,第二个进程看不到第一个进程在做什么(我们设置了事务锁定),所以它不知道另一个进程正在尝试保留项目。它认为这些项目仍然可用,也会保留它们。

我们考虑过应用程序锁定,但我们担心等待、延迟等。因此,我们想出的另一个解决方案是一个处理队列中的预订的进程。先到先得。一次只有一个进程读取队列。不同的进程可以添加到队列中,但我们不再需要担心两个进程同时尝试保留相同的产品。只有一个过程将进行预订。我希望在 SQL 内完成这一切,但这可能是不可能的。

免责声明:这可能是一种选择,但使用 Service Broker 序列化请求的建议可能是更好的解决方案。

如果您不能使用事务,但需要您的存储过程return立即得到结果,有一些方法可以在单个语句中安全地更新记录。

DECLARE @ProductId INT = 123
DECLARE @Quantity INT = 5

UPDATE Inventory
SET Available = Available - @Quantity
WHERE ProductId = @ProductId
AND Available >= @Quantity

IF @@ROW_COUNT > 0
BEGIN
  -- Success
END

在幕后,仍然有一个事务伴随着锁发生,但它只是覆盖了这一条语句。

如果您需要在一条语句中更新多条记录(保留多个产品ID),您可以使用OUTPUT子句捕获哪些记录更新成功,然后与原始请求进行比较。

DECLARE @Request TABLE (ProductId INT, Quantity INT)
DECLARE @Result TABLE (ProductId INT, Quantity INT)
INSERT @Request VALUES (123, 5), (456, 1)

UPDATE I
SET Available = Available - R.Quantity
OUTPUT R.ProductId, R.Quantity INTO @Result
FROM @Request R
JOIN Inventory I
    ON I.ProductId = R.ProductId
    AND I.Available >= R.@Quantity

IF (SELECT COUNT(*) FROM @Request) = (SELECT COUNT(*) FROM @Result)
BEGIN
    -- Success
END
ELSE BEGIN
    -- Partial or fail. Need to undo those that may have been updated.
END

无论如何,您需要彻底考虑错误处理和撤消方案。

如果您将预订与库存分开存储并将“可用”定义为“Inventory.OnHand - SUM(Reserved.Quantity)”,则此方法不可取。

(关于为什么可能会出现这种糟糕方法的评论。)