我正在将记录插入SQL Server表,然后选择自动增量ID,如下所示:
(@p0 int,@p1 nvarchar(8))INSERT INTO [dbo].[Tag]([Some_Int], [Tag])
VALUES (@p0, @p1)
SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]
(这是使用Linq-to-SQL生成的)。出于某种原因,当我使用具有Serializable隔离级别的TransactionScope对象在事务中运行此代码时,SQL Server会引发死锁错误。我分析了死锁图事件,发现所涉及的两个进程都在等待另一个进行转换操作,因为我理解以下信息:
<resource-list>
<keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176">
<owner-list>
<owner id="processc9be40" mode="RangeS-S"/>
</owner-list>
<waiter-list>
<waiter id="processc9ae38" mode="RangeI-N" requestType="convert"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176">
<owner-list>
<owner id="processc9ae38" mode="RangeS-S"/>
</owner-list>
<waiter-list>
<waiter id="processc9be40" mode="RangeI-N" requestType="convert"/>
</waiter-list>
</keylock>
</resource-list>
我的理解是,在第一个进程完成插入和选择标识之前,事务范围将阻止第二个进程执行插入。然而,情况似乎并非如此。任何人都可以通过线程安全的方式阐明实现我需要的最佳方法吗?
- 的更新 -
注意;我99%确定两个进程之间没有共享连接,因为每个进程都创建一个新的DataContext来与数据库通信。
- 再次更新 -
Remus Rusanu指出,一些遗漏的信息与问题有关,我试图根据死锁图报告简化场景,但我在这里扩展了解释。在我执行插入之前,我在相关表上执行存在查询以确定标记是否已存在。如果是的话我结束了交易。如果不是,则插入应该继续,然后我在具有Some_Int
作为主键的表上执行此处未示出的更新,尽管更新纯粹是针对最后修改的值。 Tag表具有由auto inc ID和Some_Int组成的聚簇索引也很重要。我不认为这最后一条信息具有相关性,因为我尝试将表更改为仅将auto inc字段作为主键/聚簇索引无效。
感谢。
答案 0 :(得分:7)
有问题的'转换'是'lock convert' from RangeS-S to RangeI-N,与“CONVERT”函数无关。您已将RangeS-S锁定放在PK_Tag_1索引上这一事实表明您正在做的不仅仅是INSERT。在尝试插入之前,您的交易是否有任何机会首先检查新记录是否“存在”?
答案 1 :(得分:0)
检查查询中使用的isolationLevel。请注意,TransactionScope默认使用 Serializable 隔离级别(http://msdn.microsoft.com/en-us/library/ms172152.aspx)。尝试将事务的隔离级别更改为Read Commited。
答案 2 :(得分:-1)
您根本不需要交易。 scope_identity()
函数将返回最后在同一范围内创建的id,因此如果在获取id之前执行了另一个插入,则没有问题,因为它位于不同的范围内。