无休止的争论:SQL Server中的原子插入,锁定和事务.它在这里结束

Iam*_*mIC 14 sql-server concurrency locking sql-server-2008

编辑:我的问题是,"为什么我的第一个代码示例有效?" 请继续阅读......

编辑1:毫无疑问,唯一约束是确保不会发生重复的正确方法.这是给定的.但是,有时我们需要知道我们正在尝试重复输入.此外,这篇文章不仅仅是处理重复.

这可能是对SO的100多个问题的重复.我已经阅读了关于原子更新,锁和并发的简单问题的无穷无尽的混淆和矛盾.

我看到博客和专家在这些方面存在广泛的分歧.在这里,我根据人们建议的各种解决方案提供测试代码,指出结果,陈述我的观点,并邀请您的评论.

上下文:我正在运行SQL Server 2008 Express SP2.

我创建了以下测试表:

create table dbo.Temp (Col int)
Run Code Online (Sandbox Code Playgroud)

由于我们想要测试SQL代码的想法而不是约束,因此该表故意不对其进行约束.

我在2个然后3个查询窗口中同时运行以下命令:

declare @i int
set @i = 0

while @i < 5000 begin
    set @i = @i + 1
    update dbo.temp set Col = (SELECT Col from dbo.Temp) + 1
end
Run Code Online (Sandbox Code Playgroud)

我可以看到,我没有使用任何显式锁定.所有数据库设置均为默认值 我检查了Col的值,它是所需的数字:25,000.没有错过.

由于SQL Server是ACID,"A"告诉我们单个语句是以原子方式执行的.因此,基于上述内容,我们可以同意那些说如上所述的简单更新不需要锁定的人.

接下来,我在3个查询窗口中同时运行以下命令:

while @i < 5000 begin
    set @i = @i + 1
    insert into dbo.temp select @i where not exists
      (select 1 from dbo.temp where Col = @i)
end
Run Code Online (Sandbox Code Playgroud)

尽管这是一个陈述,但结果并不正确.缺少值,重复值和> 5k行.

接下来,我在3个查询窗口中同时运行以下流行的解决方案:

declare @i int
set @i = 0

while @i < 5000 begin
    set @i = @i + 1
    insert into dbo.temp select @i where not exists 
      (select 1 from dbo.temp with (updlock) where Col = @i)
end
Run Code Online (Sandbox Code Playgroud)

结果不正确.缺少值,重复值和> 5k行.

接下来,对于那些怀疑SQL Server隐式包装事务中的单个语句(在ACID中称为"A")的人:

declare @i int
set @i = 0

while @i < 5000 begin
    set @i = @i + 1
    begin tran
    insert into dbo.temp select @i where not exists 
      (select 1 from dbo.temp with (updlock) where Col = @i)
    commit tran
end
Run Code Online (Sandbox Code Playgroud)

同样不正确的结果.

接下来,我在3个查询窗口中同时运行以下命令:

declare @i int
set @i = 0

while @i < 5000 begin
    set @i = @i + 1
    insert into dbo.temp select @i where not exists 
      (select 1 from dbo.temp with (XLOCK, ROWLOCK) where Col = @i)
end
Run Code Online (Sandbox Code Playgroud)

这很有效.仅限5k唯一值.

接下来,以下3个窗口:

declare @i int
set @i = 0

while @i < 5000 begin
    set @i = @i + 1
    merge dbo.temp as t
    using (select @i) as test (Col)
    ON (t.Col = test.Col)
    when not matched then
        insert values (@i);
end
Run Code Online (Sandbox Code Playgroud)

这很有效.仅限5k唯一值.

我的结论是:

  1. SQL Server对于单个SQL操作是原子的,而不是作为一个语句出现的操作组.
  2. 正如所示和许多人所建议的那样,UPDLOCK 无法提供所需的锁定,以保证在有争议的环境中更新完整性.
  3. 从锁定的角度来看,只有XLOCK可以保证多语句操作中的并发完整性原子性,如图所示.
  4. MERGE命令是单个命令,因此是原子命令.

请亲自测试一下.

现在,有人可以向我解释为什么我的第一个例子有效吗?:-)