选择查询的事务死锁

39

有时候,我在一个只包含Select查询的存储过程中遇到以下错误:事务 (进程 ID 91) 在锁定时发生死锁

我的初步理解是,Select查询不会锁定表,或者即使它试图查询的表正在被另一个进程更新/锁定,也不会导致死锁。但现在看来,Select查询也可能会导致死锁。

如果我将查询的隔离级别设置为读取未提交,这样是否能解决问题?

3个回答

61
我的初始理解是,一个Select查询不会锁定表,或者不会引起死锁。
这个理解是错误的。SELECT查询对它们分析的行进行共享锁定。共享锁可能与update/delete/insert语句的排他锁发生冲突。两个SELECT语句不会发生死锁,但SELECT可能会与UPDATE发生死锁。当这种死锁发生时,SELECT通常是受害者,因为它没有执行任何更新,所以总是会输掉比赛。
与任何死锁一样,您需要发布涉及的表的确切模式、确切的T-SQL语句和死锁图。请参阅如何:保存死锁图(SQL Server Profiler)。有了这些信息,您可以获得如何解决死锁的指导。

2
@Remus Rusanu 实际上,两个select语句可能会死锁。请参见https://dba.stackexchange.com/questions/152768/seeing-2-exclusive-locks-on-the-same-index-is-this-possible。你能解释一下吗? - kolobok
@RemusRusanu,这是关于两个Select语句死锁的问题:https://dba.stackexchange.com/questions/106354/deadlock-from-select-statements/106359#106359 - George K

8

就像Remus所说的那样,你会遇到死锁问题,是因为SELECT和UPDATE(或其他)操作相互死锁,而不是SELECT与SELECT相互死锁。你需要查看所有涉及该表的查询,并为这些查询创建适当的覆盖索引,这将解决你的问题。良好的覆盖索引是首选方案,而不是使用WITH(NOLOCK)表提示。

参见下面的链接,了解如何创建覆盖索引以及它如何影响死锁。


2
链接目前不可用。但我找到了这篇笔记/文章:https://sqlundercover.com/2019/03/05/using-indexing-to-solve-blocking-and-deadlocking-issues/。希望能对某些人有所帮助。 - Indian

5
如果您使用的是SQL Server 2008,您可以将隔离级别设置为读取未提交的内容以防止死锁。请参见此链接。在读取未提交或WITH(NOLOCK)时,必须注意查询返回的数据可能不是真实的!

除了读取脏(未提交)记录外,进行未提交读取还可能导致记录被跳过或读取两次!最好使用快照隔离(行版本控制)。 - Rudey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接