并发选择唯一记录

时间:2017-11-27 17:22:14

标签: c# sql sql-server

我已经阅读了其他策略,但我想知道这种同时选择唯一记录的策略是否有任何缺点。换句话说:如果两个线程同时运行并尝试在同一时间选择记录,它们将永远不会选择相同的记录。

指定表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将一次锁定一个进程的那些记录的选择和更新,因此永远不会有单个记录的双重更新。

1 个答案:

答案 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)