【发布时间】: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