NOLOCK 总是不好?

Dat*_*irl 34 sql-server locking nolock

我是一名报表开发人员,希望尽可能提高我的查询效率。我曾经与一位 DBA 一起工作,他告诉我 - 我相信是因为我总是在生产服务器上处理报告 -NOLOCK在每个查询中使用。

现在,我与一个NOLOCK在任何情况下都被禁止的 DBA 一起工作- 即使我的报告(由于几个表上缺乏索引)正在停止复制和系统更新。在我看来,在这种情况下,aNOLOCK将是一件好事。

由于我的大部分 SQL 培训都是来自不同意见的 DBA,因此我想向各种各样的 DBA 提出这个问题。

Mar*_*ith 33

它并不总是坏的。

当然,它允许您读取未提交的值(可能会回滚,因此从逻辑上不存在)以及允许多次读取值或根本不读取值等现象。

保证您不会遇到任何此类异常的唯一隔离级别是可序列化/快照。如果在扫描到达该行之前移动了一行(由于键更新),则可重复读取值可能会丢失,如果键更新导致先前读取的行向前移动,则读取提交值可以读取两次。

nolock然而,这些问题更有可能出现,因为默认情况下,在此隔离级别,当它估计有超过 64 个要读取的页面时,它将使用分配顺序扫描。除了由于索引键更新而在页面之间移动行时出现的问题类别,这些分配顺序扫描也容易受到页面拆分问题的影响(如果新分配的页面在文件中早于点如果已扫描的页面被拆分到文件中的后续页面,则已扫描或阅读两次)。

至少对于简单(单表)查询,可以阻止使用这些扫描并nolock通过简单地向ORDER BY index_key查询添加 a来获得键有序扫描 at ,以便 the 的Ordered属性IndexScantrue

但是,如果您的报告应用程序不需要绝对精确的数字并且可以容忍这种不一致的可能性更大,那么它可能是可以接受的。

但是当然您不应该在所有查询中都放弃它,希望它是一个神奇的“涡轮”按钮。除了在该隔离级别遇到异常结果或根本没有结果(“由于数据移动而无法使用 NOLOCK 继续扫描”错误)的更大概率之外,甚至在某些情况下,性能nolock 可能会更差

  • 马丁,我会建议稍微不同的词:“读取提交的值可能会被错过和读取不止一次”。在某些特殊情况下,我们可以多次检索一行。 (4认同)

Rem*_*anu 31

如果您的报告阻止了 DBA 正确的更新:您绝对不应该使用NOLOCK. 张女士说,有冲突清楚地表明,如果你用脏读,你会得到不正确的报告。

在我看来,总有比NOLOCK以下更好的选择:

  • 您的生产表是只读有效且永远不会被修改吗?将数据库标记为只读!
  • 表扫描导致锁冲突?适当地索引表,好处是多方面的。
  • 无法修改/不知道如何适当地索引?使用快照隔离
  • 无法更改应用程序以使用快照?打开读提交快照
  • 您已经测量了行版本控制的影响并有证据表明它会影响性能?你不能索引数据?你对不正确的报告没意见吗?然后至少帮自己一个忙并使用SET TRANSACTION ISOLATION LEVEL,而不是查询提示。以后修复隔离级别而不是修改每个查询会更容易。

  • 注意:开启读提交快照可能会破坏一些代码。 (6认同)

A-K*_*A-K 11

您的客户是否容忍报告中的不一致结果?如果答案是否定的,则不应使用 NOLOCK - 在并发下可能会得到错误的结果。我在这里这里这里写了几个例子。这些示例在 READ COMMITTED 和 REPEATABLE READ 下显示不一致的输出,但您可以调整它们并使用 NOLOCK 得到错误的结果。


Chr*_*cht 8

我创建的大多数报告都不是基于当前数据运行的。大多数客户的运行报告都是昨天的数据。如果是这样,你的答案会改变吗?

如果是这种情况,那么您还有另一种可能的选择:您可以从生产数据库的副本运行报告,
而不是在生产数据库上运行查询并使用锁和NOLOCK

您可以将其设置为每晚从备份中自动恢复
显然您的报告在客户站点的服务器上运行,所以我不知道设置它是否对您来说是一个可行的解决方案。
(但话又说回来......无论如何他们应该有备份,所以你需要的只是一些服务器空间来恢复它们)

我是一名内部开发人员,所以这对我来说更容易,因为我可以完全控制服务器和数据库。

您至少可以对只需要昨天和更早数据的报告执行此操作。也许一些报告将不得不保留在生产数据库上,但至少您将一些负载转移到另一个数据库(或者甚至更好,另一个服务器)。

我在工作中也有同样的情况:
我们使用这样的生产数据库副本来处理几乎所有的报告内容,但有一些查询需要今天的数据。