Sql server 无休止的争论:SQL Server中的原子插入、锁定和事务。到此为止
编辑:我的问题是,为什么我的第一个代码示例有效?请继续读 Edit1:毫无疑问,唯一约束是确保不发生重复的正确方法。这是必然的。然而,有时我们需要知道我们正在尝试一个重复的条目。此外,这篇文章不仅仅是处理副本 这可能会重复大约100多个问题。我读过无数关于原子更新、锁和并发性等简单问题的困惑和矛盾 我发现博客和专家在这些问题上存在广泛的分歧。在这里,我根据人们建议的各种解决方案提供测试代码,指出结果,陈述我的观点,并邀请您发表评论 上下文:我正在运行SQL Server 2008 Express SP2 我创建了以下测试表:Sql server 无休止的争论:SQL Server中的原子插入、锁定和事务。到此为止,sql-server,sql-server-2008,concurrency,locking,Sql Server,Sql Server 2008,Concurrency,Locking,编辑:我的问题是,为什么我的第一个代码示例有效?请继续读 Edit1:毫无疑问,唯一约束是确保不发生重复的正确方法。这是必然的。然而,有时我们需要知道我们正在尝试一个重复的条目。此外,这篇文章不仅仅是处理副本 这可能会重复大约100多个问题。我读过无数关于原子更新、锁和并发性等简单问题的困惑和矛盾 我发现博客和专家在这些问题上存在广泛的分歧。在这里,我根据人们建议的各种解决方案提供测试代码,指出结果,陈述我的观点,并邀请您发表评论 上下文:我正在运行SQL Server 2008 Express
create table dbo.Temp (Col int)
该表故意没有任何约束,因为我们想要测试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
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
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
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
可以看出,我没有使用任何显式锁定。所有数据库设置都是默认设置。我检查了Col的值,它是所需的数字:25000。没有遗漏什么
因为SQLServer是ACID,所以A告诉我们一条语句是以原子方式执行的。因此,基于上述内容,我们可以同意那些认为简单更新不需要锁的人的观点
接下来,我在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
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
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
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
结果是不正确的,尽管这是一种说法。缺少值、重复值和>5k行
接下来,我在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
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
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
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
结果不正确。缺少值、重复值和>5k行
接下来,对于那些怀疑SQL Server是否在事务中隐式包装了单个语句(也称为ACID)的人:
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
同样的错误结果
接下来,我在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
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
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
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
这起作用了。仅限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
这起作用了。仅限5k唯一值
我的结论是:
SQL Server是单个SQL操作的原子,而不是作为一条语句出现的操作组。
正如许多人所示和建议的那样,UPDLOCK在提供在有争议的环境中保证更新完整性所需的锁定方面是无效的。
从锁定的角度来看,只有XLOCK可以保证多语句操作中的并发完整性和原子性,如图所示。
MERGE命令是单个命令,因此是原子的。
请自己测试一下
现在,有人能解释一下为什么我的第一个例子有效吗- 这里最好的解决方案是使用INT标识,让SQL Server处理INT列的唯一性。。。。。。。但这是众所周知的事实,这里没有什么新鲜事,真的……当然。我总是使用索引,并允许索引真正保证唯一性。然而,很明显,有时我们需要知道行的存在并相应地进行处理,在这种情况下,我们必须进行测试。你知道为什么例1有效吗?@Marc,作为旁注,我在另一篇帖子上与你进行了对话,但我们从未结束过。这是关于我写的一个CTE。你指出它是递归的,但我不明白为什么。既然你这么做了。知道你的事,我会注意的@Mrac感谢-已发送电子邮件:-奇怪的是,一个三次受欢迎的问题作为非问题关闭。