添加主键标识列可以解决死锁问题吗?
Posted
技术标签:
【中文标题】添加主键标识列可以解决死锁问题吗?【英文标题】:Can adding a primary key identity column solve deadlock issues? 【发布时间】: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 可能会改变顺序,以至于您还没有(尚未)找到导致死锁的组合。
【参考方案1】:
解决死锁的最佳资源(仍然)在这里:http://blogs.msdn.com/b/bartd/archive/2006/09/09/deadlock-troubleshooting_2c00_-part-1.aspx。
第 4 点说:
通过数据库调优运行涉及死锁的查询 顾问。在 Management Studio 查询窗口中添加查询,更改 db 上下文到正确的数据库,右键单击查询文本并 选择“分析 DTA 中的查询”。不要跳过这一步;超过一半 我们看到的死锁问题中的大多数只需添加一个 适当的索引,以便其中一个查询运行得更快,并且 具有更小的锁占用空间。如果 DTA 推荐索引(它会说 “估计改进:%”),创建它们并监控 看看死锁是否仍然存在。您可以选择“应用建议” 从操作下拉菜单中立即创建索引,或者 将 CREATE INDEX 命令保存为脚本,以便在 维护窗口。请务必分别调整每个查询。
我知道这并不能“回答”为什么必然的问题,但它确实表明添加索引可以改变执行方式,使锁占用更小或执行时间更快,这可以显着减少死锁。
【讨论】:
【参考方案2】:最近看到这个帖子,根据以上信息希望这个帖子对你有帮助,
http://databaseusergroup.blogspot.com/2013/10/deadlocked-on-sql-server.html
【讨论】:
请注意 link-only answers 是不鼓励的,所以答案应该是寻找解决方案的终点(与另一个中途停留的参考相比,随着时间的推移往往会变得陈旧)。请考虑在此处添加独立的概要,并保留链接作为参考。以上是关于添加主键标识列可以解决死锁问题吗?的主要内容,如果未能解决你的问题,请参考以下文章
DataObjects.NET 可以支持 SQL 标识列吗?
sql 用于将列更改为作为主键的标识列的SQL代码。您必须删除密钥,删除列,将列添加回i