【问题标题】:T-SQL function not thread safe after UPDLOCKUPDLOCK 后 T-SQL 函数不是线程安全的
【发布时间】:2015-10-06 05:47:25
【问题描述】:

我有一个简单的 getNextID 过程,它检索表中的 id 值并将该值增加 1。它在构建时考虑了一些多线程,但似乎过程中的 UPDLOCK 实际上并没有使它成为线程像预期的那样安全,我试图理解为什么。这个想法是,初始选择期间的 UPDLOCK 将阻止任何其他线程执行该选择,直到过程底部的更新完成;但是,情况似乎并非如此,因为当两个线程同时触发时,我得到了重复的值。

在阅读了一些其他线程之后,我认为可能发生的情况是 UPDLOCK 正在阻止其他线程更新该行,但它并没有阻止它们在更新之前执行初始选择.所以两个线程都在执行相同的选择(检索相同的值),然后线程 2 等待线程 1 更新,然后线程 2 将行更新为相同的值。我是否了解锁的正确操作?完成线程验证的正确方法是将其全部包装在 BEGIN/COMMIT TRANSACTION 中吗?

 CREATE PROCEDURE getNextID (
    @NextNumber int OUTPUT
    ,@id_type VARCHAR(20)
    )
AS
BEGIN

    SELECT @NextNumber = (last_used_number + 1)
    FROM its_id_sequence WITH (UPDLOCK)
    WHERE id_type = @id_type

    UPDATE its_id_sequence
    SET last_used_number = @NextNumber
    WHERE id_type = @id_type
END

谢谢!

【问题讨论】:

  • 任何时候你试图改变自己的身份,你都是在打一场失败的战斗。围绕这一点有很多挑战。为什么不直接使用已经功能齐全的身份属性?如果您不喜欢间隙(这当然是完全正常的),如果您在 2012 年以上,则可以改用序列。
  • @SeanLange:我认为序列不能保证无间隙。 也许如果您在创建序列时指定no cache,但这会影响性能。我从来没有人能够合理地解释在序列中没有间隙的业务需求。
  • @BenThul 序列本身将是无间隙的。但是,如果您将其用作主键,则一旦删除了一行,它将以空白告终。 OP 在这里所做的与已经构建的序列基本相同。
  • @SeanLange:来自文档:“使用 CACHE 选项创建时,意外关闭(例如电源故障)可能会丢失缓存中的序列号。”。
  • 但不管生成无间隙数字序列的意义何在?删除一行时总会有间隙,因此无间隙编号的心态是一场永无止境的战斗。接受差距并继续前进。

标签: sql-server multithreading tsql


【解决方案1】:

另一种方法是稍微使用不同版本的更新查询,显然将整个事情扭曲成一个事务。

CREATE PROCEDURE getNextID
     @NextNumber INT          OUTPUT
    ,@id_type    VARCHAR(20)
AS
BEGIN
  SET NOCOUNT ON;

    DECLARE @NextValue TABLE (NextNumber int);

BEGIN TRANSACTION;

    UPDATE its_id_sequence
      SET last_used_number = ISNULL(@NextNumber, 0) + 1
     OUTPUT inserted.last_used_number INTO @NextValue(NextNumber)
    WHERE id_type = @id_type

    SELECT @NextNumber = NextNumber FROM @NextValue

COMMIT TRANSACTION;

END

所以你首先更新而不读取值,一旦更新然后获取值并使用它。

【讨论】:

  • 我以前从未见过 OUTPUT 插入语法。感谢那!所以使这个线程安全的最好方法是在事务中执行更新和选择。仅出于我的理解,假设事务以选择查询开始,然后进行更新(例如 SELECT (id +1),然后以 UPDATE id SET id = (id+1) 结束)。线程 2 是否能够在线程 1 提交之前执行选择,或者线程 2 会等到整个线程 1 事务完成后再开始自己的事务?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 1970-01-01
相关资源
最近更新 更多