6

我目前正在向 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 字段作为主键/聚集索引无济于事。

谢谢。

4

3 回答 3

7

有问题的“转换”是从 RangeS-S 到 RangeI-N 的“锁定转换”,与“转换”功能没有任何关系。您已经在 PK_Tag_1 索引上放置了 RangeS-S 锁这一事实表明您所做的不仅仅是 INSERT。您的事务是否会在尝试插入之前先检查新记录是否“存在”?

于 2009-06-15T11:50:59.590 回答
0

检查您的查询中使用的isolationLevel。请注意,TransactionScope 默认使用Serializable 隔离级别 ( http://msdn.microsoft.com/en-us/library/ms172152.aspx )。尝试将事务的隔离级别更改为已提交读。

于 2010-01-31T16:22:50.730 回答
-1

你根本不需要交易。该scope_identity()函数将返回最后在同一范围内创建的 id,因此如果在您获取 id 之前执行另一个插入是没有问题的,因为那是在不同的范围内。

于 2009-06-15T08:06:50.283 回答