653

有人可以解释使用with (nolock)查询的含义,什么时候应该/不应该使用它?

例如,如果您的银行应用程序具有高交易率并且某些表中有大量数据,那么在哪些类型的查询中可以使用 nolock?是否存在您应该始终使用它/从不使用它的情况?

4

16 回答 16

501

WITH (NOLOCK) 相当于使用 READ UNCOMMITED 作为事务隔离级别。因此,您将面临读取随后回滚的未提交行的风险,即从未进入数据库的数据。因此,虽然它可以防止读取被其他操作死锁,但它带来了风险。在具有高交易率的银行应用程序中,它可能不会成为您试图用它解决的任何问题的正确解决方案,恕我直言。

于 2009-03-26T17:16:28.050 回答
177

问题是更糟糕的是什么:

  • 僵局,或
  • 错误的值?

对于金融数据库,死锁比错误的值要糟糕得多。我知道这听起来倒退了,但请听我说完。数据库事务的传统示例是更新两行,从其中减去并添加到另一行。那是错的。

在财务数据库中,您使用业务交易。这意味着向每个帐户添加一行。最重要的是这些事务完成并且行被成功写入。

暂时弄错账户余额没什么大不了的,这就是日终对账的目的。由于同时使用两台 ATM,而不是未提交的数据库读取,账户透支的可能性要大得多。

也就是说,SQL Server 2005 修复了大部分NOLOCK必要的错误。因此,除非您使用的是 SQL Server 2000 或更早版本,否则您不需要它。

进一步阅读
行级版本控制

于 2009-03-26T18:08:58.547 回答
63

不幸的是,这不仅仅是读取未提交的数据。在后台,您可能最终会阅读两次页面(在页面拆分的情况下),或者您可能会完全错过这些页面。所以你的结果可能会严重偏斜。

查看Itzik Ben-Gan 的文章。这是一段摘录:

" 使用 NOLOCK 提示(或将会话的隔离级别设置为 READ UNCOMMITTED),您告诉 SQL Server 您不期望一致性,因此无法保证。请记住,“不一致的数据”不仅意味着您可能会看到后来回滚的未提交更改,或者事务的中间状态中的数据更改。这也意味着在扫描所有表/索引数据的简单查询中,SQL Server 可能会丢失扫描位置,或者您可能最终会两次获得同一行。”

于 2011-03-29T07:30:20.630 回答
56

nolock 提示合法使用的教科书示例是针对高更新 OLTP 数据库的报告抽样。

举一个话题的例子。如果一家大型美国商业街银行想要运行每小时报告,以寻找银行出现城市级别挤兑的最初迹象,则 nolock 查询可以扫描交易表,汇总每个城市的现金存款和现金取款。对于这样的报告,由回滚更新事务引起的错误的微小百分比不会降低报告的价值。

于 2009-03-26T17:55:26.583 回答
33

不知道为什么您不将金融交易包装在数据库交易中(例如当您将资金从一个帐户转移到另一个帐户时 - 您不会一次提交交易的一侧 - 这就是存在显式交易的原因)。即使您的代码听起来像是对业务事务脑死亡,但所有事务数据库都有可能在发生错误或失败时进行隐式回滚。我认为这个讨论超出了你的想象。

如果您遇到锁定问题,请实施版本控制并清理您的代码。

无锁不仅会返回错误的值,还会返回幻像记录和重复项。

一个常见的误解是它总是使查询运行得更快。如果表上没有写锁,则没有任何区别。如果表上有锁,它可能会使查询更快,但首先发明锁是有原因的。

公平地说,这里有两种特殊情况,其中 nolock 提示可以提供实用程序

1) 需要对实时 OLTP 数据库运行长查询的 2005 年之前的 sql server 数据库,这可能是唯一的方法

2) 写得很糟糕的应用程序会锁定记录并将控制权返回给 UI,并且阅读器会被无限期地阻塞。如果无法修复应用程序(第三方等)并且数据库是 2005 之前的版本或无法打开版本控制,则 Nolock 在这里会很有帮助。

于 2010-03-03T17:01:05.540 回答
24

NOLOCK等同于READ UNCOMMITTED,但微软表示您不应将其用于UPDATEorDELETE语句:

对于 UPDATE 或 DELETE 语句:此功能将在 Microsoft SQL Server 的未来版本中删除。避免在新的开发工作中使用此功能,并计划修改当前使用此功能的应用程序。

http://msdn.microsoft.com/en-us/library/ms187373.aspx

本文适用于 SQL Server 2005,因此NOLOCK如果您使用的是该版本,则存在对的支持。为了使您的代码适应未来(假设您决定使用脏读),您可以在存储过程中使用它:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

于 2009-03-26T17:43:25.847 回答
23

您可以在仅读取数据时使用它,并且您并不真正关心是否可能会取回尚未提交的数据。

读取操作可以更快,但我不能说多少。

一般来说,我建议不要使用它——读取未提交的数据充其量可能有点令人困惑。

于 2009-03-26T17:15:14.497 回答
18

另一种通常没问题的情况是在报告数据库中,其中数据可能已经老化并且不会发生写入。但是,在这种情况下,管理员应该通过更改默认隔离级别在数据库或表级别设置该选项。

在一般情况下:当您非常确定可以读取旧数据时,您可以使用它。要记住的重要一点是,它很容易出错。例如,即使在您编写查询时没问题,您确定将来数据库中不会更改某些内容以使这些更新更重要吗?

我也会认为这在银行应用程序中可能不是一个好主意。或库存应用程序。或者您正在考虑交易的任何地方。

于 2009-03-26T17:19:44.890 回答
15

简单的答案 - 只要您的 SQL 没有更改数据,并且您的查询可能会干扰其他活动(通过锁定)。

对于用于报告的任何查询,都值得考虑,尤其是在查询时间超过 1 秒的情况下。

如果您有针对 OLTP 数据库运行的 OLAP 类型的报告,这将特别有用。

不过,要问的第一个问题是“我为什么要担心这个?” 根据我的经验,当有人处于“尝试任何东西”模式时,通常会伪造默认锁定行为,这是一种不太可能出现意外后果的情况。这往往是过早优化的情况,并且很容易被嵌入到应用程序中“以防万一”。重要的是要了解你为什么这样做,它解决了什么问题,以及你是否真的有问题。

于 2009-03-26T18:08:33.650 回答
14

简短的回答:

WITH (NOLOCK)在具有聚集索引的表上的 SELECT 语句中使用。

长答案:

WITH(NOLOCK) 通常被用作加快数据库读取速度的神奇方法。

结果集可以包含尚未提交的行,这些行通常稍后会回滚。

如果将 WITH(NOLOCK) 应用于具有非聚集索引的表,则当行数据流式传输到结果表时,其他事务可以更改行索引。这意味着结果集可能会丢失行或多次显示同一行。

READ COMMITTED 增加了一个额外的问题,即多个用户同时更改同一单元格的单个列中的数据损坏。

于 2016-02-05T15:54:17.980 回答
10

WITH (NOLOCK我的 2 美分 -当您需要生成报告时使用 ) 是有意义的。在这一点上,数据不会有太大变化并且您不想锁定这些记录。

于 2010-07-13T01:48:50.920 回答
9

如果您正在处理金融交易,那么您将永远不想使用nolock. nolock最好用于从具有大量更新的大型表中进行选择,并且您不在乎您获得的记录是否可能已过时。

对于财务记录(以及大多数应用程序中的几乎所有其他记录)nolock会造成严重破坏,因为您可能会从正在写入的记录中读取数据而无法获得正确的数据。

于 2009-03-26T17:17:58.717 回答
8

我曾经为要做的事情检索“下一批”。在这种情况下,确切的项目并不重要,我有很多用户运行相同的查询。

于 2009-03-26T17:17:09.220 回答
7

当您对“脏”数据没问题时,请使用 nolock。这意味着 nolock 也可以读取正在修改和/或未提交数据的过程中的数据。

在高事务环境中使用它通常不是一个好主意,这就是为什么它不是查询的默认选项。

于 2009-03-26T17:16:58.033 回答
7

我使用 with (nolock) 提示,特别是在具有高活动性的 SQLServer 2000 数据库中。但是,我不确定 SQL Server 2005 是否需要它。我最近应客户 DBA 的请求在 SQL Server 2000 中添加了该提示,因为他注意到了很多 SPID 记录锁。

我只能说,使用提示并没有伤害到我们,而且似乎已经解决了锁定问题。该特定客户的 DBA 基本上坚持我们使用提示。

顺便说一句,我处理的数据库是企业医疗索赔系统的后端,所以我们谈论的是数百万条记录和 20 多个表在许多连接中。我通常为连接中的每个表添加 WITH (nolock) 提示(除非它是派生表,在这种情况下您不能使用该特定提示)

于 2009-03-26T17:24:44.473 回答
5

最简单的答案是一个简单的问题——你需要你的结果是可重复的吗?如果是,那么 NOLOCKS 在任何情况下都不合适

如果您不需要可重复性,那么 nolocks 可能很有用,特别是如果您无法控制连接到目标数据库的所有进程。

于 2016-05-20T09:49:09.507 回答