【发布时间】:2012-05-24 12:15:55
【问题描述】:
我在 SQL Server 中有一个表,它由在不同会话中同时运行的存储过程同时进行 CRUD:
|----------------|---------|| <some columns> | JobGUID ||----------------|---------|
流程如下:
- 生成 GUID。
- 将一些记录插入上述共享表中,并使用步骤 1 中的 GUID 对其进行标记。
- 对第 2 步中的所有记录执行一些更新。
- 选择步骤 3 中的记录作为 SP 输出。
存储过程中的每个 select / insert / update / delete 语句都有一个 WHERE JobGUID = @jobGUID 子句,因此该过程仅适用于它在步骤 2 中插入的记录。但是,有时当相同的存储过程在不同的连接,共享表会发生死锁。这是来自 SQL Server Profiler 的死锁图:
不会发生锁定升级。我尝试将 (UPDLOCK, ROWLOCK) 锁定提示添加到所有 DML 语句和/或将过程的主体包装在事务中并使用不同的隔离级别,但它没有帮助。共享表上的 RID 锁仍然相同。
之后我发现共享表没有主键/标识列。一旦我添加它,死锁似乎就消失了:
alter table <SharedTable> add ID int not null identity(1, 1) primary key clustered
当我删除主键列时,死锁又回来了。当我重新添加它时,我无法再重现死锁。
那么,问题是,主键标识列真的能够解决死锁还是只是巧合?
更新: 正如@Catcall 建议的那样,我尝试在现有列上创建一个自然聚集的主键(不添加标识列),但仍然遇到相同的死锁(当然,这次是钥匙锁而不是 RID 锁)。
【问题讨论】:
-
@MartinSmith:是的,它有一个非唯一的非聚集索引。
-
SP 运行在什么事务隔离级别?
-
@DanielRenshaw:在 SP 正文中运行
DBCC USEROPTIONS会显示read committed。 -
删除主键约束时是否有任何索引消失?
-
死锁提示似乎是说在一行上有一个排他锁,但另一个 SID 正试图抢占同一行上的锁。我认为这只是巧合。如果没有聚集索引(或聚集 PK),表将是一个堆。因此,对于在每个过程执行期间哪些行/页被锁定,您基本上受堆访问的支配。集群 PK 可能会改变顺序,以至于您还没有(尚未)找到导致死锁的组合。
标签: sql-server concurrency primary-key deadlock