是NOLOCK(Sql Server提示)不好的做法吗?


125

我的业务是制作并非关键任务的网站和应用程序->例如。银行软件,太空飞行,重症监护监控应用程序等。您明白了。

因此,使用大量的免责声明,在某些Sql语句中使用NOLOCK提示是否不好?几年前,一位SQL管理员提出建议,如果我对“脏读”感到满意,则应该使用NOLOCK,这会给我系统带来更多的性能,因为每次读都不会锁定表/行/其他。

还告诉我,如果遇到僵局,这是一个很好的解决方案。因此,我开始遵循这种想法几年了,直到一个sql专家帮助我提供了一些随机代码,并注意到sql代码中的所有NOLOCKS。礼貌地责骂我,他试图向我解释(为什么这不是一件好事),我有点迷路了。我觉得他的解释的实质是“这是一个解决更严重问题的创可贴解决方案……特别是在您遇到僵局的情况下。因此,请解决问题的根源。

我最近对此进行了一些搜索,发现了这篇文章

那么,请问一些sql db专家老师能启发我吗?


我不明白,Sam,如果这是一个阅读量很大的网站,那就是说使用快照隔离。但是然后您说是这样做的,这很不好吗?还是只是使用NOLOCK?
Pure.Krome

Answers:


67

使用NOLOCK提示,该SELECT语句的事务隔离级别为READ UNCOMMITTED。这意味着查询可能会看到脏数据和不一致的数据。

通常这不是一个好主意。即使对于基于Web的任务关键型应用程序来说,这种脏读行为也可以,但NOLOCK扫描可能会导致601错误,由于缺少锁定保护,由于数据移动,该错误将终止查询。

我建议阅读“ 快照隔离有帮助时以及何时受到伤害 ”-MSDN建议在大多数情况下使用“ READ COMMITTED SNAPSHOT”而不是“ SNAPSHOT”。


1
Rex,请随时添加有关快照隔离的说明。
Sam Saffron

2
是的,Sam说的是快照隔离,而您建议阅读已提交快照。我变得非常困惑:P(而且我还没有深入研究这些文章!)
Pure.Krome

2
有时是有用的,但通常不适用于生产。我经常使用它来提取数据样本以进行测试或生成报告,在这些报告中,我主要关心的是粗略的数量级,而脏读无关紧要。
TimothyAWiseman

NOLOCK ==我不在乎是否遗漏了已提交的行,是否包括未提交的行,在极少数情况下,同一行被返回不止一次,在极少数情况下,返回的行与我的查询不匹配。(请参阅blogs.msdn.com/b/sqlcat/archive/2007/02/01/…,从与此主题相关的另一个SO q'n中找到)
Andrew Hill

106

在处理Stack Overflow之前,我反对NOLOCK您可能会使用SELECTwith NOLOCK并从过时或不一致的数据中获取结果的原则。要考虑的一个因素是,另一个进程可能正在从同一表中选择数据,同时可以插入/更新多少条记录。如果这种情况经常发生,那么除非您使用诸如的数据库模式,否则很有可能出现死锁READ COMMITED SNAPSHOT

从那以后,NOLOCK在见证了它如何提高SELECT性能以及消除海量负载的SQL Server上的死锁之后,我改变了对使用的看法。有时您可能并不在乎您的数据并非完全100%提交,即使结果可能已过时,您也需要快速返回结果。

在考虑使用NOLOCK以下方法时问自己一个问题:

我的查询中是否包含一个包含大量INSERT/ UPDATE命令的表,并且我是否关心查询返回的数据在给定时刻是否可能缺少这些更改?

如果答案是否定的,则使用NOLOCK来提高性能。


我只是NOLOCK在代码库中对Stack Overflow 的关键字进行了快速搜索,发现了138个实例,因此我们在很多地方都使用了它。


7
IMO,这有点简单。可以通过使用覆盖索引来消除死锁,从而减轻了聚集索引的压力。
米奇小麦,

8
我不希望削弱良好指数覆盖率的重要性。有时,使用NOLOCK的查询可以在具有大量插入/更新的表上的索引实现的基础上,提高性能。即使以数据不正确或丢失为代价,堆栈溢出的查询速度也至关重要。
杰夫·达尔加斯

9
显然,使用可以得到重复的行NOLOCK。这意味着我必须拒绝你的回答。抱歉。
ErikE 2011年

1
仅从SELECT覆盖索引读取的@MitchWheat A 可能导致死锁。SPID 1)SELECT从覆盖索引开始。SPID 2)启动UPDATE表的。然后更新将更新覆盖索引。UPDATE达到由锁定的索引范围,SELECT并被阻止。SPID 1)仍在搜索覆盖索引,找到被锁定的范围并被UPDATE阻止。死锁。没有任何办法可以解决该死锁(捕获SQL Server错误1205,并自动重试或使用NOLOCK)除外
Ian Boyd

2
我认为关于此答案需要注意的重要一点是,它适合当前的问题。根据您的应用程序,陈旧/未提交/重复/丢失数据的风险可能不值得权衡。
整体开发人员

20

如果您不关心脏读(即在主要为READ的情况下),那NOLOCK很好。

但是,请注意,大多数锁定问题是由于查询工作负载没有“正确的”索引所致(假设硬件由任务决定)。

上师的解释是正确的。通常,它是解决更严重问题的临时解决方案。

编辑:我绝对不建议应使用NOLOCK。我想我应该清楚地表明这一点。(我只会在极端情况下使用它,因为我分析过它是可以的)。举个例子,前一段时间,我研究了一些用NOLOCK填充的TSQL,以尝试减轻锁定问题。我全部删除了它们,实现了正确的索引,所有的死锁都消失了。


3
嗯..我还是不明白。这样就可以了,但是它的形式也很差。
Pure.Krome

假设您从不关心脏读,那么它不会受到伤害。但通常是治疗症状而不是原因的情况……
Mitch Wheat

2
好吧,我认为它的公正性并不低,我认为您没有解决使用nolock时出现的任意错误。偶尔在您的网站上出现空白的错误页面是不好的,这确实是一种糟糕的形式。我不喜欢这样的断言:“如果您不关心脏读,那就没问题了……”即使您不关心脏读也没有好处
Sam Saffron 2009年

查询生成异常时未获取重试逻辑的空白页。当查询执行结果异常时,您的站点上会发生什么?您到处都有重试逻辑吗?
Sam Saffron

进行一个非常优化的查询,您知道该查询命中了正确的索引。然后添加nolock提示,并观察它变得更快。如果您不关心脏读,那么使用nolock绝对不会伤害自己。
Hardwareguy

13

怀疑是“专家”谁曾在高流量方面有过任何经验...

当人们查看完整加载的页面时,网站通常是“肮脏的”。考虑从数据库中加载然后保存已编辑数据的表单吗?人们对脏读的继续进行是一种荒谬的做法。

就是说,如果在选择中构建了多个层,则可能会构成危险的冗余。如果您要处理的是金钱或状态情况,则不仅需要读/写交易数据,还需要适当的并发解决方案(大多数“专家”都不会理会)。

另一方面,如果您对网站进行了高级产品搜索(例如,可能不会被缓存并且需要一些密集的内容),并且您建立的网站拥有的并发用户数量多于其他用户(例如“专家”还没有),将它后面的所有其他过程都束之高阁是多余的。

了解其含义,并在适当的时候使用它。如今,数据库几乎始终是您的主要瓶颈,并且精通使用NOLOCK可以为您节省数千美元的基础架构。

编辑:它不仅可以帮助解决僵局,还可以使其他人等到完成为止,反之亦然。

在EF4中使用NOLOCK提示?


10

没有一个答案是错误的,但是可能有些混乱。

  • 查询单个值/行时,使用NOLOCK 始终是不好的做法-您可能永远不想显示不正确的信息,甚至可能对不正确的数据采取任何措施。
  • 显示粗略的统计信息时,NOLOCK可能非常有用。以SO为例:锁来读取问题的确切视图数或标签的确切问题数是胡说八道。现在,没有人关心您是否错误地声明了标记为“ sql-server”的3360个问题,并且由于事务回滚,一秒钟后就有3359个问题。

我完全不同意你的第一点。如果要查询单个值/行,并且为该行指定唯一的ID,并且知道没有其他进程可以访问它,那么使用nolock是完全可以接受的,并且可以减少并发应用程序中的阻塞。

1
不,这不对。该行可能由于其他原因而发生更改,例如,插入另一行会拆分页面。正确建立索引,读取已提交快照和快照隔离几乎总是更好的主意。
Mark Sowul '04年

1
@tuseau,如果您“知道”没有其他进程将访问该行,则执行锁定操作不会阻止任何操作,因此您(实际上)不会花费任何费用,
Andrew Hill

2

作为专业的开发人员,我会说这取决于。但是我绝对会遵循GATS和OMG Ponies的建议。知道自己在做什么,知道什么时候有帮助,什么时候疼,

阅读提示和其他不良想法

是什么让您更深入地了解sql server。我通常遵循SQL提示为EVIL的规则,但是不幸的是,每当我厌倦了强迫SQL Server执行操作时,我都会不时使用它们……但这是极少数情况。

路加


2

当应用程序支持希望使用SSMS来回答生产服务器中的广告请求时(不是通过报告来解决的),我要求他们使用nolock。这样,“主要”业务就不会受到影响。


2

我同意一些有关NOLOCK提示的评论,尤其是那些说“在适当时使用它”的评论。如果应用程序编写不佳并且使用并发方式不当–则可能导致锁升级。高度事务性的表由于其性质也一直被锁定。拥有良好的索引覆盖范围将不利于检索数据,但是将ISOLATION LEVEL设置为READ UNCOMMITTED可以。我也相信,在变化的性质是可预测的情况下,使用NOLOCK提示在许多情况下是安全的。例如,在制造业中,当旅行者的作业要经过不同的过程并插入大量测量值时,您可以使用NOLOCK提示安全地对完成的作业执行查询,这样可以避免与将PROMOTED或EXCLUSIVE锁放在桌子上的其他会话发生冲突/页。在这种情况下,您访问的数据是静态的,但它可能驻留在一个非常具有事务性的表中,该表每分钟具有数亿条记录和数千个更新/插入。干杯


2

我相信使用nolock实际上永远是不正确的。

如果要读取单个行,则正确的索引意味着您将不需要NOLOCK,因为单个行的动作可以快速完成。

如果您要读取除临时显示以外的其他任何内容的行,并且希望能够重复结果或通过产生的数字进行辩护,则NOLOCK不适用。

NOLOCK是“我不在乎此答案是否包含重复的行,已删除的行或由于回滚而从未插入的行”的代理标记”

在NOLOCK下可能出现的错误:

  • 完全不返回匹配的行。
  • 单行被多次返回(包括同一主键的多个实例)
  • 返回不匹配的行。

在noLock选择运行时可能导致页面拆分的任何操作都可能导致这些情况发生。几乎所有操作(甚至删除操作)都可能导致页面拆分。

因此:如果您“知道”在运行时行不会更改,请不要使用nolock,因为索引将允许有效的检索。

如果您怀疑查询正在运行时行可以更改,并且您担心准确性,请不要使用nolock。

如果由于死锁而考虑使用NOLOCK,请检查查询计划结构以进行意外的表扫描,跟踪死锁并查看为什么会发生死锁。在写操作周围使用NOLOCK可能意味着先前死锁的查询都可能会写错答案。


2

更好的解决方案是:

  • 将数据(使用日志复制)复制到报告数据库。
  • 使用SAN快照并安装数据库的一致版本
  • 使用具有更好的基本事务隔离级别的数据库

之所以创建SNAPSHOT事务隔离级别,是因为MS失去了对Oracle的销售。Oracle使用撤消/重做日志来避免此问题。Postgres使用MVCC。未来,MS的Heckaton将使用MVCC,但距离生产就绪还差几年。


上面有一个错字。我的意思是说“更好的基本事务隔离机制”。
pwy

1
SNAPSHOT事务隔离级别是MS的发明。基本上,它将数据放在TEMPDB的临时表中。该数据库在盒子上的所有数据库之间共享。因此,您将尽可能在TEMPDB中使用SSD。这可能比其他选项要少。
2014年

1

NOLOCK通常被用作提高数据库读取速度的一种神奇方法,但我尝试尽可能避免使用它。

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

错误或结果集可以为空,缺少行或多次显示同一行。

这是因为其他事务正在读取数据的同时移动数据。

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


-2

在现实生活中,您会遇到已经写入系统并向表中添加索引,然后大大降低14gig数据表的数据加载速度的情况,有时您不得不在报表上使用WITH NOLOCK,并在月末进行处理,以使总功能(总和,计数等)不对行,页,表进行锁定并确定整体性能。在新系统中容易说的是,永远不要使用WITH NOLOCK并使用索引-但是添加索引会严重降低数据加载的级别,然后,当我被告知,改变代码库以删除索引,然后批量加载然后重新创建索引时-如果您要开发一个新系统,那就很好。但是当您已经有一个系统时就没有。


1
您在说什么
马克斯·亚历山大·汉纳
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.