2

我之前已经多次阅读和使用 with(nolock) 提示,但我对特定案例有疑问。

就我而言,我有一组引用和更新一个数据库的代码。
此代码旨在以单线程方式运行。几个月前,他们决定在不更改代码的情况下使其成为多线程。他们的做法是让每个不同的“代码处理器”管理不同的商店组。

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

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

一切看起来都很好而且很漂亮,直到我的同事来找我这篇文章把我吓坏了......然后我读了那个......

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

4

3 回答 3

2

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

READ_COMMITTED_SNAPSHOT 数据库选项开启

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

于 2015-12-15T19:58:52.477 回答
1

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

我喜欢看死锁图看看冲突在哪里,然后看代码中的操作顺序,以及哪些索引被用来访问/修改数据,看看我是否可以调整删除死锁风险。

于 2015-12-15T19:43:39.797 回答
1

看起来您的选择查询是死锁的原因。这就是可能发生的事情。您稍后可能会选择和更新记录。因此,当更新超过 5000 条记录时,sql server 使用锁升级并锁定整个表而不是锁定行。

如果您的程序是这样的 -

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

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

解决方案 -

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

  2. 使用快照隔离

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

于 2015-12-17T15:17:05.530 回答