【问题标题】:SQL Locking Scope, race condition preventionSQL Locking Scope,竞争条件预防
【发布时间】:2011-12-05 21:44:40
【问题描述】:

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

【问题讨论】:

  • 您可以添加一个额外的表吗?如果是这样,就有可能想出一个阻塞更少的解决方案。
  • @Martin 不幸的是,目前不允许添加额外的表,但如果没有其他可接受的性能方法,可能会提出一个案例。这之前是通过 MyTable 上的 Serializable 隔离级别和全表锁实现的。我的任务是优化,因为该解决方案导致了很多性能问题。
  • 有什么理由不能将其更改为identity 列吗?
  • @Martin 我无法将其更改为 Identity 列,因为这是属于不同的现成应用程序的数据库。此应用程序中的代码对该特定列进行了大量处理,并将其用于许多不适当的事情。我担心如果我不以类似于应用程序插入数据的方式插入数据,我会导致严重的下游问题。

标签: sql sql-server-2008 locking


【解决方案1】:
SELECT MAX(value) 
FROM MyTable 
WITH (XLOCK, HOLDLOCK)

应该足够了。 HOLDLOCK 提供了可序列化的语义,这意味着将在备份主键的索引末尾的范围上进行键范围锁定。 XLOCK 表示两个并发事务不能同时获取这个锁。

这确实意味着您的 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

【讨论】:

  • 我认为单独的表策略在这里不起作用,因为我的代码不是向该表提交数据的唯一代码,除非我遗漏了什么。通过我的代码重构和缩小的锁定范围,我能够将插入时间从 5 秒减少到大约半秒。谢谢!
  • 马丁,你是如何测试你的答案的?您是否对两个或多个连接进行了压力测试?
  • @AlexKuznetsov - 不。在这里看不到任何竞争条件,因为它将在索引末尾的范围内锁定RangeX-X(资源(ffffffffffff))。 And this is incompatible with just about everything
  • 我要补充一点,如果您坚持 Martin 的回答 (SELECT MAX(value) FROM MyTable WITH (XLOCK, HOLDLOCK),您可以考虑修改您的原始程序以在插入 MyTable 后立即提交到 MyTable . 插入 MyTable 后,您对 id 的所有权就完成了。更新辅助表时无需锁定其他人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-07
  • 2016-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多