我有一个将数据插入数据库(SQL Server 2008)的进程,该数据库的模式我无法修改。该表有一个int PK,但没有自动增量。所以,我需要获取最大的id,增加它然后插入(并返回新的id。)此事务还需要同时更新许多其他表。我显然试图避免同时插入的竞争条件。
Begin Transaction (Read Committed)
DECLARE @MyVar int;
--here be the race condition
SET @MyVar = (( SELECT MAX(value) FROM MyTable WITH (ROWLOCK, XLOCK, HOLDLOCK)) + 1);
INSERT INTO MyTable ....
UPDATE MyOtherTable SET Val = @MyVar WHERE WhatEver
SELECT MyRetValName = @MyVar
INSERT INTO MyThirdTable ...
Commit Transaction
事务隔离级别和表锁定提示是否足以防止竞争条件或我是否需要UPDLOCK而不是ROWLOCK? (如果插入失败,我有一个单独的'重试'过程。)
答案 0 :(得分:0)
SELECT MAX(value)
FROM MyTable
WITH (XLOCK, HOLDLOCK)
应该足够了。 HOLDLOCK
给出了可序列化的语义,这意味着将在备份主键的索引末尾的范围内进行键范围锁定。 XLOCK
表示2个并发事务不能同时获取此锁。
这意味着您的insert
程序的所有并发呼叫者将在交易期间被阻止。
如果您可以添加新表,那么阻塞性较小的解决方案就是创建另一个包含identity
列的表,并插入其中,如下所示。
CREATE TABLE dbo.Sequence(
val int IDENTITY (10000, 1) /*Seed this at whatever your current max value is*/
)
GO
CREATE PROC dbo.GetSequence
@val AS int OUTPUT
AS
BEGIN TRAN
SAVE TRAN S1
INSERT INTO dbo.Sequence DEFAULT VALUES
SET @val=SCOPE_IDENTITY()
ROLLBACK TRAN S1 /*Rolls back just as far as the save point to prevent the
sequence table filling up. The id allocated won't be reused*/
COMMIT TRAN