我目前正在向 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 生成的)。出于某种原因,当我使用具有可序列化隔离级别的 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 字段作为主键/聚集索引无济于事。
谢谢。