9

我不时遇到一些长时间运行的查询超时问题和死锁。

我想知道什么时候最适合使用 NOLOCK 以及在哪里使用?

我是否在更新和插入中使用它?或阅读?

4

6 回答 6

6

请注意,您可以在每个表的基础上指定 nolock。

我通常在复杂的 SELECT 查询中使用 nolock,但仅用于几乎从未更改的小查找表和仅显示数据。您知道列出当前半年价格的表格,或将 id 查找到字符串等。这些内容只会随着重大更新而改变,之后服务器通常会定期重新启动。

这显着提高了性能,在最繁忙的时候减少了死锁的机会,更重要的是,在最坏的情况下,对于涉及大量表的查询(这是合乎逻辑的,他们必须获得更少的锁,而那些 sidetables几乎在任何地方都经常使用,通常从需要锁定的 7-8 个表减少到 4 个表)

但要非常小心添加它,不要急于添加,也不要常规添加。用得好不会疼,用得不好会疼得厉害。

不要将它用于高度关键的东西,计算的东西等,因为它会变得不一致,任何迟早会导致写入的东西。

另一个这样的优化是 ROWLOCK,它只锁定行级别。这在更新(或删除)行彼此不相关的表时非常有用,例如您只放入日志记录的表(并且插入的顺序无关紧要)。如果您有一个方案,即在事务结束时将日志记录写入某个表,这也可以大大加快速度。

如果您的数据库的写入百分比相对较低,则可能不值得。我的读写比率低于 2:1。

我在处理此问题时保存的一些 URL:

http://www.developerfusion.com/article/1688/sql-server-locks/4/

于 2009-05-03T09:43:45.090 回答
5

SQL Server 中有四个事务隔离级别:

  1. 阅读未提交
  2. 阅读已提交
  3. 可重复阅读
  4. 可序列化

对于它所应用的表,NOLOCK 相当于“未提交读”。这意味着您可以看到将来可能回滚的事务中的行,以及许多其他奇怪的结果。

尽管如此,nolock 在实践中仍然非常有效。尤其是对于显示稍有错误的数据并不是世界末日的只读查询,例如业务报告。我会避免它靠近更新或插入,或者通常靠近决策代码的任何地方,特别是如果它涉及发票。

作为 nolock 的替代方案,请考虑“已提交的读取快照”,它适用于具有大量读取和较少写入活动的数据库。您可以通过以下方式打开它:

ALTER DATABASE YourDb SET READ_COMMITTED_SNAPSHOT ON;

它适用于 SQL Server 2005 及更高版本。这就是 Oracle 默认的工作方式,也是 stackoverflow 本身使用的方式。甚至还有一个关于它的编码恐怖博客条目。

PS 长时间运行的查询和死锁也可能表明 SQL Server 正在使用错误的假设。检查您的统计信息或索引是否已过期:

SELECT 
    object_name = Object_Name(ind.object_id),
    IndexName = ind.name,
    StatisticsDate = STATS_DATE(ind.object_id, ind.index_id)
FROM SYS.INDEXES ind
order by STATS_DATE(ind.object_id, ind.index_id) desc

统计数据应在每周维护计划中更新。

于 2009-05-03T10:13:45.907 回答
3

Use nolock as a last resort. Most deadlock problems can be fixed by tuning the queries and/or tuning the indexes. I think I've seen one deadlock in the last 5 years that couldn't be fixed by tuning one of the two.

Also note that NOLOCK is only honoured on select statements. Data modifications will always lock, that behaviour cannot be changed. So if you're got a writer/writer deadlock (quite common), no lock won't help at all.

Also be aware that nolock, in addition to returning dirty data can result in duplicate rows (rows read twice from the underlying table) and missing rows (rows in the underlying table that weren't read at all).

Nolock essentially means to SQL Server 'I don't mind if my results are slightly inaccurate'

Snapshot isolation is an option. Just make sure that you test carefully first as the increased load on TempDB can be quite severe, depending how frequent and long your transactions are. Also note that while you won't see deadlocks in snapshot isolation, you can get update conflicts. Again, test and make sure that your apps work properly and can handle any errors that they get.

于 2009-05-03T19:04:58.533 回答
2

在可以接受脏读和幻像记录时使用它,例如,您可能有定期运行的非关键报告,其中信息的准确性不是主要驱动因素,而是查看记录量或其他一些指标

于 2009-05-03T09:21:11.540 回答
2

当可以读取脏数据时,您应该使用 nolock。可能对数据库进行大量更改的大型事务可能仍在进行中,使用 nolock 只会返回它到目前为止设置的数据。如果该事务然后回滚您正在查看的数据可能是错误的。因此,您应该只在返回的内容可能是错误的情况下才使用它。

死锁是一个常见问题,但十分之九是完全由开发人员问题引起的。我会专注于寻找死锁的原因,而不是使用 nolock。很可能只是一个事务以与所有其他事务不同的顺序做事。解决这个问题可能会使您的所有问题都消失。

于 2009-05-03T09:48:50.757 回答
1

对于没有读锁的事务一致视图,建议在 SQL Server 中启用快照隔离。

这与 NOLOCK 略有不同,因为当您读取信息时,结果总是反映已提交数据的版本,而不是查看未提交数据的可能性。这提供了与 NOLOCK(无“读”锁)相同的锁定并发性,结果更清晰。

即使具有事务一致性,也应始终牢记,您在显示时继续显示或使用的数据无论如何都可能是错误的或过时的。我见过太多人认为,如果他们使用数据的速度足够快,或者如果他们在查询/事务中使用它就可以了。这是荒谬的——我认为,可重复的一致性级别一开始就不应该实现,因为它只会鼓励不良行为。它们在 Oracle 中不存在。

就我个人而言,我喜欢为某些非关键数据视图和报告禁用锁定,因为它给系统带来的负担更少,而且提供稍微不准确的结果的可能性很小也不是问题。

在初始开发方面,利用可重复的读取一致性级别并犯下诸如为用户输入保留开放事务之类的罪行可能对开发人员来说更容易一些,但几乎总是会导致主要的道路“障碍”,以实现合理扩展应用程序的任何希望.

我的观点是,最好的方法始终是“仔细检查”仍然必须为真的条件,以便将更新应用于任何数据。

坏的:

UPDATE myaccount SET balance = 2000

更好的:

UPDATE myaccount SET balance = balance + 2000

更好的是:

UPDATE myaccount SET balance = 2000 WHERE balance = 0 AND accountstatus = 1

最后,应用程序必须检查行数以确保在向用户提供成功反馈之前实际更新了预期的行数。

于 2009-05-03T10:22:48.880 回答