有关SQL Server死锁的所有文档都讨论了操作1锁定资源A然后尝试访问资源B并且操作2锁定资源B并尝试访问资源A的情况。
但是,我经常看到select和更新之间的死锁,甚至在我们繁忙的应用程序中的多个选择之间。我发现死锁跟踪输出的一些细节非常难以理解但我真的只想了解什么可能导致两个单独操作之间的死锁。当然,如果一个选择具有读锁定,则更新应该在获得独占锁之前等待,反之亦然?
这种情况发生在SQL Server 2005上,而不是我认为这会产生影响。
答案 0 :(得分:20)
这可能发生,因为select会锁定两个不同的索引,同时更新会以相反的顺序锁定相同的索引。 select需要两个索引,因为第一个索引不包含它需要访问的所有列;更新需要两个索引,因为如果更新索引的键列,则需要对其进行锁定。
http://blogs.msdn.com/bartd/archive/2006/09/25/770928.aspx有一个很棒的解释。建议的修复包括添加一个索引,该索引覆盖select所需的所有列,切换到快照隔离,或显式强制select获取通常不需要的更新锁。
答案 1 :(得分:13)
我曾在SQL-Server-Performance.com为一篇关于Advanced SQL Server locking的好文章添加了书签。那篇文章超出了你提到的经典僵局情况,可能会让你对你的问题有所了解。
答案 2 :(得分:13)
我很惊讶没有人提到WITH (UPDLOCK)
锁定提示。如果您遇到涉及例如死锁的死锁,那将非常有用。两个并行运行的select-insert对。
在SQL Server中,如果使用WITH (UPDLOCK)
发出选择,则第二个选择将等到第一个选择完成。否则他们会获得共享锁,当他们同时尝试升级到独占锁时,他们会死锁。
答案 3 :(得分:5)
单个查询之间的锁定可能会发生,因为它们会锁定单个行,而不是整个表:
更新查询在表中的几行上获取更新锁定,并且select查询在表中的某些其他行上获取读锁定。然后,更新查询尝试对读取锁定的行进行更新锁定,并且select查询尝试对更新锁定的行进行读锁定。
升级锁可能会变得更加复杂,即数据库决定事务锁定的单行太多,因此应该将其升级为锁定表的一部分或整个表。这意味着锁可能会影响不直接参与查询的行。
答案 4 :(得分:5)
我的猜测是select-statement获取了一个读锁,当你带有update-statement时,它需要升级到一个写锁。
升级到写锁需要删除所有其他读锁(它们的select-transactions完成)。但是如果另一个进程已经有了升级到写锁的好主意,那么你突然有两个进程在等待彼此释放读锁,因此它们可以获得写锁。
如果使用select-for-update(UPDLOCK),它将从头开始获取写锁,然后你就没有死锁问题。
答案 5 :(得分:1)
在事务和隔离级别上正确阅读:对于稍微密集但相当彻底且技术中立的工作,请参阅Principles of Transaction Processing。它震撼了我的世界(给了我很多头疼!)。
我不确定您遇到的问题,或者您使用的隔离级别。但请考虑一下:对于所有数据库引擎都知道,如果你在一个事务中读取,那么它如何判断你以后是否要进行写操作?高隔离级别在读取完成时需要锁定,可能在整个表上以防止幻像读取,因为数据可能会影响稍后的写入。
您是否希望数据库等待任意长时间对数据进行独占锁定?从整体上看一下您的隔离级别,以及您是否不必要地将一系列读取作为独立事务运行。虽然确定你可以忍受的污秽读数并不总是那么容易......
答案 6 :(得分:0)