tra*_*ark 8 sql-server deadlock concurrency nolock
我有一个处理并发 SELECT 和 DELETE 的表。我的 SELECT 语句出现了一些死锁。我假设来自其他事务的 DELETE 正在获取排他锁并与 SELECT 的共享锁冲突。
在并发 SELECT 和 DELETE 的情况下,它可能看起来像这样(在 MS Paint 中绘制,因为我试图变得专业......):
编辑:上面的“触发器”不是指实际的数据库触发器,而是指应用程序行为。
我在 DBA Stack Exchange 上四处闲逛,发现我可以SELECT myTable WITH (NOLOCK)
阻止共享锁。我正在考虑使用它,但我知道有很多警告和问题,所以我想验证我的决定或在必要时更换它。
我是 WITH (NOLOCK) 的新手,所以这是我从这个有用的网站学到的东西:
WITH (NOLOCK) 表提示检索行,而无需等待其他正在读取或修改相同数据的查询来完成其处理。
这些报价来自同一个链接。在每一项下,我都描述了我的想法,得出结论认为该行为不会影响我。
通常,经常使用显式表提示被认为是一种应避免的不良做法。特别是对于NOLOCK表提示,读取未提交的数据,读完后可能会回滚,会导致Dirty read,在未提交的数据读取过程中读取正在修改或删除的数据时会出现这种情况,从而导致数据你读到的可能会有所不同,或者甚至从未存在过。
脏读:我认为我不需要关心这个,因为 SELECT 实际上并不是因为脏读而无效。任何导致脏读的 DELETE 都会触发一个新的 SELECT 来纠正最终结果。 我认为两个 SELECT 结果都是有效的,尽管一个只在几毫秒内有效。
WITH (NOLOCK) 表提示也会导致不可重复读取;当需要多次读取相同的数据并且在这些读取过程中数据发生变化时,就会发生这种读取。在这种情况下,您将阅读同一行的多个版本。
不可重复读:我的 SELECT 只有该表中的一个 SELECT 语句,所以我认为这不是问题。
幻读也可能是使用 WITH(NOLOCK) 表提示的结果,当插入新记录的事务回滚时,您将获得更多记录,或在删除现有数据的事务回滚时获得更少记录.
幻读:与脏读相同
当其他事务将您尚未读取的数据移动到您已扫描的位置,或将新页面添加到您已扫描的位置时,可能会出现另一个问题。在这种情况下,您将错过这些记录,并且不会在返回的结果中看到它们。如果另一个事务将您已经扫描的数据移动到您尚未读取的新位置,您将读取该数据两次。
我不确定这是否会影响我的用例,但我认为仅删除几行不会导致这种级别的页面改组。我真的不知道我在谈论这个话题,所以也许我离题了。
这个用例是真正可以安全使用的罕见用例之一,WITH (NOLOCK)
还是我应该考虑一些危险?
在我写这个问题的时候,我只是把它扔给了 Dev 来试一试,似乎仍然在某个地方发生了死锁。我仍然想了解这种方法是否有效,以及它是否仍然是整个死锁解决方案的一部分。
我发现这个建议使用 SNAPSHOT 隔离级别,但是当我的用例可以承受诸如脏读之类的副作用时,我不确定是否应该承担性能损失。
免责声明:我不是 DBA,而是具有几年 DB 经验的软件开发人员。我只是“教科书级”熟悉锁定、页面、提示等的内部工作原理,所以请耐心等待,如果我能以任何方式改进问题,请告诉我。如果需要,我很高兴澄清。
Dav*_*oft 15
我不是 DBA,而是具有几年 DB 经验的软件开发人员。我只是“教科书级”熟悉锁定、页面、提示的内部工作原理,
那么您应该使用 SNAPSHOT 隔离,或者将您的数据库设置为 READ COMMITTED SNAPSHOT,因为编写正确、可扩展、无死锁的代码从根本上更简单。
NOLOCK/ReadUncommited 以可预测的方式放宽并发模型是一种常见的误解。IE,它只是允许您在可能最终回滚的状态下读取数据。不是这种情况。行在更改时有时需要四处移动,并且 NOLOCK 查询可能会错过此类行或多次读取它们。或者,当一个非聚集索引和基础表被更新而另一个没有被更新时,NOLOCK 查询可能会读取一个非聚集索引和基础表。 这两者都可能导致错误的结果。
READ COMMITTED SNAPSHOT/SNAPSHOT 的成本是行需要一点额外的簿记。有一个额外的 14 字节字段添加到更新和删除的行以指向前一个行版本,行版本存储在 TempDb 或用户数据库中。但也有性能优势。您的工作负载可以更轻松地扩展,因为锁定争用更少,并且会话能够更并发地运行。
您无法使用 WITH (NO LOCK) 查询提示并保证始终拥有 100% 准确的数据。我过去维护过确实使用它的查询并且很少遇到问题,但我个人的选择(除非数据准确性不是问题)是避免它。请参阅Microsoft 的 David Browne对我的问题的回答,以了解无法保证数据准确性的决定性原因。
简而言之,您选择的行可能是不断变化的数据结构的一部分,因为被删除的其他行也是同一数据结构的一部分,例如特定索引的 B 树,两行都是其中的一部分.
如果您真的不关心读取可能会回滚的未提交数据(例如,如果 INSERT 正在进行并被死锁)未提交记录会显示、不可重复读取中的重复数据或丢失记录,那么您不在乎足够的数据准确性使用 WITH (NOLOCK) 提示应该是一个问题。但是通常建议将服务器隔离级别更改为更宽松的级别,例如 READ UNCOMMITTED 隔离,而不是使用查询提示。
归档时间: |
|
查看次数: |
1313 次 |
最近记录: |