什么是SQL Server中的“ with(nolock)”?


610

with (nolock)在您应该/不应该使用查询时,有人可以解释使用查询的含义吗?

例如,如果您有一个银行业务应用程序,该应用程序具有高事务处理率,并且某些表中的数据很多,那么在哪种类型的查询中就可以了?在某些情况下,您应该始终使用它/从不使用它吗?




1
这是使用NOLOCK的含义的极好的摘要blogs.msdn.com/b/davidlean/archive/2009/04/06/…–
Bryan

Answers:


461

WITH(NOLOCK)等同于使用READ UNCOMMITED作为事务隔离级别。因此,您将冒着读取未提交的行并随后回滚的风险,即从未进入数据库的数据。因此,尽管它可以防止读取因其他操作而陷入僵局,但它存在风险。在具有高事务处理率的银行应用程序中,它可能不是您要尝试使用IMHO解决的任何问题的正确解决方案。


156
大多数银行应用程序可以安全地使用nolock,因为它们在业务意义上是事务性的。您仅写入新行,而从未更新它们。
乔纳森·艾伦,

49
@ Grauenwolf-插入但未提交的行仍可能导致脏读。
萨斯曼

16
@ Saasman-如果您从不回退事务,那没关系。而且,对于仅插入表,回滚的机会微乎其微。而且,如果确实发生了这种情况,您仍将在日末差异报告中全部解决。
乔纳森·艾伦,2009年

1
我根深蒂固。我假设添加了无锁提示。举例:
罗斯·布什

11
如果NOLOCK与a一起使用,SELECT如果在进行选择时曾经将数据插入(或更新)到表中,则冒着多次返回相同行(重复数据)的风险。
伊恩·博伊德

170

问题是更糟的是:

  • 僵局,或
  • 错误的值?

对于财务数据库,僵局要比错误的值严重得多。我知道这听起来很倒霉,但请听我说。DB事务的传统示例是更新两行,从两行中减去然后加到另一行。那是错的。

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

暂时使帐户余额有误不是什么大不了的事情,这就是日对帐的目的。由于一次使用两个ATM而不是从数据库中未提交读操作,导致帐户透支的可能性更大。

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

进一步阅读
行级版本控制


22
暂时使帐户余额有误不是什么大事?如果该交易是您从自动提款机中提取现金而没有透支额度的交易,该怎么办?
学习

13
@Learning:如果您在某人向您的卡收费之前30秒钟拿出钱怎么办?直到所有的交流都变得如此疯狂,透支才是生活中的事实。
乔纳森·艾伦,

6
+1在金融应用程序中(无处不在,不仅在银行中),既没有更新也没有删除。甚至错误的操作也会通过插入记录来纠正。这个英文名词是什么?是“ storno”吗?Google没帮助我回答这个问题
Gennady VaninГеннадийВанин2010年

7
延迟进入...僵局应该被发现并重试。脏读会产生后果
-gbn

4
@JonathanAllen只是个骗子,这个词是UTmost,不是UP。
吉米D

57

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

查看Itzik Ben-Gan的文章。摘录如下:

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


5
再往下一点,他解释了如何两次获得同一行:我将重新创建表T1,以便使用选项IGNORE_DUP_KEY将col1上的聚集索引定义为唯一索引。这意味着col1中不能存在重复值,并且尝试插入重复键不会使事务失败并不会产生错误,而只会产生警告。 如果您不使用此怪异选项,则可能不必担心会两次获取行
JanHudecek 2012年

即使没有连接选项,您仍然可以获得重复行-例如,如果您的查询利用的索引不是唯一的。但是,这并不是nolock独有的。
RMD

54

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

举一个典型的例子。如果一家大型美国大街银行想要运行每小时报告,以查找该银行运行的城市级别的最初迹象,则nolock查询可以扫描交易表,汇总每个城市的现金存款和现金提取。对于这样的报告,由回滚更新事务导致的很小百分比的错误不会减少报告的价值。


31

不知道为什么不将金融交易包装在数据库交易中(例如,当您将资金从一个帐户转移到另一个帐户时-您一次不承诺交易的一方-这就是存在显式交易的原因)。即使您的代码听上去很像商务事务的头脑,但所有的交易数据库都有可能在发生错误或失败时进行隐式回滚。我认为这种讨论很麻烦。

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

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

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

公平地说,以下是两种特殊情况,其中nolock提示可能会提供实用程序

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

2)编写不良的应用程序会锁定记录并将控制权返回给UI,并且读者将被无限期地阻止。如果无法修复应用程序(第三方等)并且数据库为2005年前版本或无法打开版本控制,则Nolock在这里可能会有所帮助。


11
我同意您的观点,即不应使用NOLOCK来弥补编写得不好的代码。但是,我认为您对Johnathan的攻击是没有根据的,因为他从未真正提及数据库事务。他只是指出,财务应用程序通常不允许编辑记录(显然有一些例外情况)。在您的资金转帐的例子,他说,如果你是这将是奇怪变异的账户余额的价值,而不是增加借记/贷记到各种各样的台账。
chrnola 2014年

19
当资金从一个帐户转移到不同银行的另一个帐户时,您会怎么想?一些超级数据库在美国银行的桌子上锁了,而在富国银行的桌子上锁了吗?否。将财务交易记录到每个交易记录中,然后进行最后的处理,证明所有记录都匹配。
乔纳森·艾伦

24

NOLOCK等价于READ UNCOMMITTED,但是Microsoft说您不应将其用于UPDATEDELETE声明:

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

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

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

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED


21

您可以在仅读取数据时使用它,而不必担心是否会取回尚未提交的数据。

读取操作可能会更快,但我无法真正说出多少。

通常,我建议不要使用它-读取未提交的数据充其量可能会使您感到困惑。


1
如果您提到SQL Server 2005具有行版本控制,这样就不再需要nolock了,那就太好了。
乔纳森·艾伦

好吧,即使在SQL Server 2005中,“ with(nolock)”仍然占有一席之地-但是好处越来越小,这是事实。
marc_s 2009年

18

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

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

我还将提出第二种观点,即在银行应用程序中这可能不是一个好主意。或库存应用。或任何您正在考虑交易的地方。


4
作为从事银行应用程序工作的人,我不得不说无锁不是问题。事务记录(即已插入但从未更新或删除的行)令人惊讶地抵抗了读取未提交数据的问题。
乔纳森·艾伦,2009年

15

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

对于用于报表的任何查询都值得考虑,特别是如果查询花费了超过1秒的时间。

如果您有针对OLTP数据库运行的OLAP类型的报告,则该功能特别有用。

但是,第一个要问的问题是“我为什么为此担心?” 根据我的经验,当某人处于“尝试任何方式”模式时,常常会欺骗默认的锁定行为,这是一种不可能发生意外后果的情况。常常是过早优化的情况,很容易“以防万一”将其嵌入应用程序中。重要的是要了解为什么要这样做,它要解决什么问题以及您是否确实有问题。


11

简短答案:

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

长答案:

WITH(NOLOCK)通常被用作提高数据库读取速度的一种神奇方法。

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

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

READ COMMITTED(读取已提交)增加了一个额外的问题,即数据在单个列中损坏,多个用户同时更改了同一单元格。


2
如果不改变最终决定,则读取未提交的数据是可以接受的。例如,如果您要计划零售商店在未来6个月内将要赚多少钱,那么看到Tammy真正买了3条纸巾就买了2条纸巾不会改变您对未来的看法。
乔纳森·艾伦

1
如果您的过滤器现在包括了,那么运行查询时您的数据将不准确。如果您的过滤器仅包含历史数据,则使用READ UNCOMITTED 将完全不受影响。有一个原因将其包含在ANSI标准中。
乔纳森·艾伦

2
永不说永不。如果您需要数据是100%准确的,则不应使用nolock。对我来说,通常不是这样。向用户呈现数据时,到他们对数据起作用时,它可能已经发生了任何变化,但很可能没有发生变化,并且锁争用不值得响应延迟。
Perposterer

让我们坚持值得存在的系统。别忘了塔米(Tammy)的大脑完全像样,所以使用数据库记录微小的纸巾采购,没人会注意到,这充其量是荒谬的。我们的重点是实际上很重要的系统,例如按时给人们付款,对紧急情况做出响应等。对WITH(NOLOCK)的使用可以被编程任何值得存在的系统的人们理解,这是非常有益的。当一个人可以做得比数据库更好时,请考虑重新利用这些资源。
WonderWorker

10

我的2美分- WITH (NOLOCK在需要生成报告时可以使用)。此时,数据不会有太大变化,并且您也不想锁定这些记录。


2
我认为WITH(NoLOCK)如果您不关心当前更改会更好。
阿卜杜勒·萨博尔2013年

9

如果您要处理金融交易,则永远不会使用nolocknolock最好用于从具有大量更新的大表中选择,并且您不在乎所获取的记录是否可能已过时。

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


5
令人惊讶的是,在处理财务数据时,这不是问题。由于行永远不会编辑,并且帐户在一天结束时仍会被对帐,因此暂时读取虚假数据不会做任何事情。
乔纳森·艾伦,

8

我曾经检索过“下一批”要做的事情。在这种情况下,哪个项目是没有关系的,并且我有很多用户在运行相同的查询。


1
我们做一些类似的事情来呈现后台任务,并要完成一系列工作。如果在到达特定记录时它不再存在/不符合选择标准,那么它将继续进行下一个记录。
TripeHound 2014年

7

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

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


3
您唯一需要的时间是在高事务环境中。如果您的表大部分处于空闲状态,那么您将不会获得任何收益。
乔纳森·艾伦,2009年

7

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

我只能说,使用提示并没有伤害我们,似乎使锁定问题得以解决。那个特定客户的DBA基本上坚持我们使用提示。

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


1
SQL Server 2005添加了“行版本控制”,这将大大减少对nolock的需求。我们最近进行了升级,并不在培训我们的数据库管理员以停止使用它们。
乔纳森·艾伦,2009年

5

最简单的答案是一个简单的问题-您是否需要结果可重复?如果是,则在任何情况下都不适合使用NOLOCKS

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.