不断检查 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)”,则此方法不可取。
(关于为什么可能会出现这种糟糕方法的评论。)
我们有一个进程导致脏读错误。所以,我正在考虑将其重新设计为一个队列,其中有一个进程通过队列。
我的想法是创建一个 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)”,则此方法不可取。
(关于为什么可能会出现这种糟糕方法的评论。)