SQL锁定范围,竞争条件预防

时间:2011-12-05 21:44:41

标签: sql sql-server-2008 locking

我有一个将数据插入数据库(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? (如果插入失败,我有一个单独的'重试'过程。)

1 个答案:

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