首页 > 解决方案 > 在 SQL Server 中,如何防止多线程应用程序读取同一行两次?

问题描述

我有一组触发器,它们在更新或删除时在单独的事件表中创建一个条目。此表包含 ID、表名和已更改/删除的数据。

我有一个多线程应用程序 .NET core 3.0 应用程序,它定期选择 ID 最低的记录,处理数据将处理后的数据发送到 API。一旦完成,它就会从表中删除该行。

问题是,如果 SELECT 由进程 1 完成,然后进程 2 在进程 1 完成 DELETE 之前完成 SELECT,则同一行可能会被不同的进程读取两次。

由于事件表没有“锁定”列,我希望通过某种行锁定和 WITH (readpast) 来完成此操作。但是,由于 SELECT 和 DELETE 在单独的事务中,我不确定这是否合适。

鉴于当前的设置,关于如何实现这一点的任何建议,或者引入锁定列是理想的方式?

标签: sqlsql-serverlocking

解决方案


我认为,大卫建议的另一种更安全的方法是分割你的队列。

对于双进程系统,您可以将队列划分为奇数项和偶数项(基于 ID),并让一个进程处理偶数项,另一个进程处理奇数项。

这是你永远不会在两个进程之间出现死锁/竞争情况。

例子:

DECLARE @ProcessID INT = 0 -- 0 for "Process 1" and 1 for "Process 2"
SELECT TOP 1 * FROM [Queue]
WHERE ID % 2 = @ProcessID -- 2 is the number of processes
ORDER BY ID ASC

从上面可以看出,这可以扩展到任意数量的进程。

优点

  • 完全避免死锁和竞争条件。
  • 不需要原子操作。

缺点

  • 不保证处理顺序。
  • 如果一个进程停止,那么一半的项目将不会被处理。

推荐阅读