在tsql是一个插入与选择语句安全的并发? [英] In tsql is an Insert with a Select statement safe in terms of concurrency?

查看:144
本文介绍了在tsql是一个插入与选择语句安全的并发?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我对此SO问题的回答中我建议使用一个插入语句,其中一个选择增加一个值,如下所示。

In my answer to this SO question I suggest using a single insert statement, with a select that increments a value, as shown below.

Insert Into VersionTable 
(Id, VersionNumber, Title, Description, ...) 
Select @ObjectId, max(VersionNumber) + 1, @Title, @Description 
From VersionTable 
Where Id = @ObjectId 

我建议这样做,因为我相信此语句在并发方面是安全的,因为如果同一对象标识的另一个插入同时运行,则不会有重复的版本号。

I suggested this because I believe that this statement is safe in terms of concurrency, in that if another insert for the same object id is run at the same time, there is no chance of having duplicate version numbers.

我是否正确?

推荐答案

正如Paul写道:不,它不安全,我想添加经验证据:具有一个字段 ID 的表 Table_1 和一个值 0的记录。然后在两个Management Studio查询窗口中同时执行以下代码

As Paul writes: No, it's not safe, for which I would like to add empirical evidence: Create a table Table_1 with one field ID and one record with value 0. Then execute the following code simultaneously in two Management Studio query windows:

declare @counter int
set @counter = 0
while @counter < 1000
begin
  set @counter = @counter + 1

  INSERT INTO Table_1
    SELECT MAX(ID) + 1 FROM Table_1 

end

然后执行

SELECT ID, COUNT(*) FROM Table_1 GROUP BY ID HAVING COUNT(*) > 1

在我的SQL Server 2008上,一个ID( 662 )创建了两次。因此,应用于单个语句的默认隔离级别不足。

On my SQL Server 2008, one ID (662) was created twice. Thus, the default isolation level applied to single statements is not sufficient.

,用 BEGIN TRANSACTION COMMIT 包装 INSERT t修复它,因为事务的默认隔离级别仍然是 READ COMMITTED ,这是不够的。请注意,将事务隔离级别设置为 REPEATABLE READ 是不够的。要使上述代码安全的唯一方法是添加

Clearly, wrapping the INSERT with BEGIN TRANSACTION and COMMIT won't fix it, since the default isolation level for transactions is still READ COMMITTED, which is not sufficient. Note that setting the transaction isolation level to REPEATABLE READ is also not sufficient. The only way to make the above code safe is to add

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

。然而,这在我的测试中每时每刻都会造成死锁。

at the top. This, however, caused deadlocks every now and then in my tests.

编辑:我发现的唯一解决方案是安全死锁(至少在我的测试中)是明确地锁定表(默认事务隔离级别在这里是足够的)。当心,此解决方案可能会杀死性能:

The only solution I found which is safe and does not produce deadlocks (at least in my tests) is to explicitly lock the table exclusively (default transaction isolation level is sufficient here). Beware though; this solution might kill performance:

...loop stuff...
    BEGIN TRANSACTION

    SELECT * FROM Table_1 WITH (TABLOCKX, HOLDLOCK) WHERE 1=0

    INSERT INTO Table_1
      SELECT MAX(ID) + 1 FROM Table_1 

    COMMIT
...loop end...

这篇关于在tsql是一个插入与选择语句安全的并发?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆