我在 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 锁)。