我已经阅读了其他策略,但我想知道这种同时选择唯一记录的策略是否有任何缺点。换句话说:如果两个线程同时运行并尝试在同一时间选择记录,它们将永远不会选择相同的记录。
指定表TableName
,其中包含ProcessID
列,其DEFAULT值为NULL
。
为清晰起见而编辑 :ProcessID
不是标识或自动增量字段。它可能是也可能不是进程表的FK,但尚未确定。它只是此时代码的标识符。
var myGUID = Guid.NewGuid();
ExecuteCommand("UPDATE TableName SET ProcessID = @myGUID WHERE ProcessID IS NULL", myGUID);
var myThreadsUniqueRecords = Query("SELECT * FROM TableName WHERE ProcessID = @myGUID", myGUID);
我删除了处理异常,回滚和跟踪的代码,如果ProcessID先前已更新,则为孤立。我也意识到GUID碰撞的可能性几乎是不可能的,除非有明显的理由提出,否则我想忽略它。
问题的目标是 :有没有理由将这些记录返回到两个不同的线程?我的理解是SQL Server将一次锁定一个进程的那些记录的选择和更新,因此永远不会有单个记录的双重更新。
答案 0 :(得分:2)
虽然您的方法很安全,但我建议采用略有不同的方法。首先,您不需要更新然后单独选择,而是使用OUTPUT语句:
UPDATE TableName
SET ProcessID = @myGUID
OUTPUT INSERTED.*
WHERE ProcessID IS NULL
这将在单个语句中更新并选择受影响的行。
考虑到这一说法 - 你实际上并不需要使用任何指南。您可以使用偶数布尔字段指示当前是否正在处理当前项目:
UPDATE TableName
SET IsProcessing = true
OUTPUT INSERTED.*
WHERE IsProcessing = false
它将与使用guid一样安全,因为两个事务将无法同时更新同一行。您可能希望添加指示项目何时开始处理的列,并引入超时以处理工作程序崩溃时的情况。此外,您可能不希望同时获取表中的所有行 - UPDATE TOP(x)有助于:
UPDATE TOP (10) TableName
SET IsProcessing = true, ProcessingStarted = SYSUTCDATETIME()
OUTPUT INSERTED.*
WHERE IsProcessed = false AND
(IsProcessing = false OR DATEDIFF(minute, ProcessingStarted, SYSUTCDATETIME()) > 60)