With(NoLock) 提示在我的情况下是否危险?

Is the With(NoLock) hint dangerous in my case?

我之前已经多次阅读并使用过 with(nolock) 提示,但我对特定情况有疑问。

在我的例子中,我有一组引用和更新一个数据库的代码。
此代码旨在以单线程方式 运行。 几个月前,他们决定在不更改代码的情况下使其成为多线程。他们采用的方法是让每个不同的 "code processor" 管理不同的商店组。

例如,处理器 1 负责存储 1 到 20,处理器 2 负责存储 21 到 40,依此类推。

一切似乎都很好,直到我们开始陷入僵局。死锁总是在页面上……如果锁只在行上,我们将永远不会遇到死锁,因为来自一个处理器的数据永远不会与来自另一个处理器的数据发生冲突。基于该理论,我决定在每个不需要锁定的 select 上放置 With(Nolock) 提示(尚未放入产品中)......这使我的死锁在我的实验室中消失了。

一切看起来都很好,直到我的同事带着 THIS 文章来找我,这让我吓坏了...... 然后我读了 THAT...

在我的情况下使用 With(Nolock) 提示是否有危险...处理器之间的数据永远不会发生冲突?

是的,如果您正在收集必须准确的数据,很可能会出现问题 - 您可能会获取 "dirty"/不正确的数据,然后将其保留。您是否查看过索引是否可以改正死锁问题?通常可以通过控制对数据页的访问模式来解决死锁情况,这由可用索引控制。

我喜欢做的是查看死锁图,看看冲突在哪里,然后查看代码中的操作顺序,以及哪些索引被用于 access/modify 数据,以看看我是否可以调整以消除死锁风险。

收回您的 nolock 提示并将您的数据库置于快照隔离级别。

READ_COMMITTED_SNAPSHOT 数据库选项开启

有关详细信息,请参阅 https://technet.microsoft.com/en-us/library/ms191242(v=sql.105).aspx。但是,请注意,保存 tempDB 的磁盘在快照隔离方面体验更高 I/O。

看起来您的 select 查询是死锁的原因。这就是可能发生的事情。稍后您可能会 select 创建和更新记录。因此,当更新超过 5000 sql 的记录时,服务器使用锁升级并锁定完整的 table 而不是锁定行。

如果你的程序是这样的-

select .......
--some coding here----
update statement

如果另一个线程在您的更新语句之前使用 select 语句,您的更新语句将因为共享锁而被阻塞,并且您的更新语句将等待共享锁被释放,同时另一个进程发出更新语句。由于您应用了共享锁,第二个进程更新语句将被阻止。因此会发生死锁,因为两个线程都在等待对方。

解决方案-

  1. 使用 UPDLOCK 提示 - 这将允许共享锁但不允许更新锁。将您的 select 语句转换为 -> select * 来自我的table 和 (UPDLOCK,ROWLOCK) RowLOCK 提示将保持锁定在行级别而不是页面或 table 级别。这将减少死锁

  2. 使用快照隔离

请注意不要在 select 语句中使用 with (NOLOCK),因为这会导致未提交数据的脏读,这可能是错误的。