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

Posted

技术标签:

【中文标题】NOLOCK(Sql Server 提示)是不好的做法吗?【英文标题】:Is the NOLOCK (Sql Server hint) bad practice? 【发布时间】:2010-11-29 23:51:27 【问题描述】:

我从事制作网站和应用程序的业务,这些网站和应用程序 不是关键任务 -> 例如。银行软件、太空飞行、重症监护应用等。你懂的。

那么,有了这么大的免责声明,在某些 Sql 语句中使用 NOLOCK 提示是不是很糟糕?几年前,一位 Sql 管理员建议我应该使用 NOLOCK,如果我对“脏读”感到满意,这会给我的系统带来更多性能,因为每次读取都不会锁定表/行/随便什么。

我还被告知,如果我遇到死锁,这是一个很好的解决方案。所以,我开始遵循这个想法几年,直到 Sql 大师帮助我编写一些随机代码并注意到我的 sql 代码中的所有 NOLOCKS。我被礼貌地骂了一顿,他试图向我解释(为什么这不是一件好事),我有点迷路了。我觉得他的解释的本质是'这是一个更严重问题的创可贴解决方案..特别是如果你遇到僵局。因此,解决问题的根源'。

我最近在谷歌上搜索了一下,发现了this post。

那么,请一些 sql db guru sensei 赐教我吗?

【问题讨论】:

我不明白 Sam,你是说如果它是一个阅读量很大的网站,请使用快照隔离。但是你说是这样做的,这很糟糕吗?还是只是他们对 NOLOCK 的使用? 【参考方案1】:

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

在见证了NOLOCK 如何提高SELECT 的性能以及消除大规模加载的SQL Server 上的死锁之后,我改变了我对使用NOLOCK 的看法。有时您可能并不关心您的数据并未完全 100% 提交,并且您需要快速返回结果,即使它们可能已过时。

在考虑使用NOLOCK时问自己一个问题:

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

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


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

【讨论】:

IMO,这有点简单。可以通过使用覆盖索引来消除死锁,从而减轻聚集索引的压力。 我不想削弱良好索引覆盖率的重要性。有时,使用 NOLOCK 的查询可以在具有大量插入/更新的表上的索引实现的收益之上增加额外的性能。即使以不准确或丢失数据为代价,Stack Overflow 上的查询速度也至关重要。 显然one can get duplicate rows 使用NOLOCK。这意味着我必须否决您的答案。对不起。 @MitchWheat A SELECT,仅从覆盖索引中读取,可能会导致死锁。 SPID 1) 从覆盖索引开始一个SELECT。 SPID 2) 启动表的UPDATE。然后更新移动到更新覆盖索引。 UPDATE 到达由 SELECT 锁定的索引范围并被阻止。 SPID 1) 仍在寻找覆盖索引,找到被UPDATE 锁定的范围并被阻止。 死锁。没有什么能解决这个死锁(除了捕获 SQL Server 错误 1205,并自动重试,或使用NOLOCK 我认为这个答案需要注意的重要一点是它适合手头的问题。根据您的应用程序,陈旧/未提交/重复/丢失数据的风险可能不值得权衡。【参考方案2】:

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

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

我建议阅读When Snapshot Isolation Helps and When It Hurts - MSDN 建议在大多数情况下使用 READ COMMITTED SNAPSHOT 而不是 SNAPSHOT。

【讨论】:

Rex,请随时添加关于快照隔离的说明。 是的,Sam 说的是快照隔离,而您建议的是读取提交的快照。我很困惑:P(而且我还没有深入研究这些文章!) 它偶尔有用,但通常不适用于生产。我经常使用它来提取数据样本进行测试或生成报告,我主要关心粗略的数量级,而脏读无关紧要。 NOLOCK == 我不在乎是否遗漏了已提交的行,包括未提交的行,在极少数情况下多次返回同一行,并且在极少数情况下返回不匹配的行我的查询。 (见blogs.msdn.com/b/sqlcat/archive/2007/02/01/…,从另一个关于这个主题的SO q'n中找到)【参考方案3】:

如果您不关心脏读(即在主要是 READ 的情况下),那么 NOLOCK 就可以了。

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

而且大师的解释是正确的。对于更严重的问题,这通常是一种创可贴的解决方案。

编辑:我绝对不是建议使用 NOLOCK。我想我应该清楚地说明这一点。 (我只会在我分析过它没问题的极端情况下使用它)。举个例子,不久前我在一些 TSQL 上工作过,这些 TSQL 已经撒了 NOLOCK 以尝试缓解锁定问题。我将它们全部删除,实现了正确的索引,所有的死锁都消失了。

【讨论】:

嗯..我还是不明白。所以没关系,但它的形式也很差......你在说什么吗? 假设您从不关心脏读,那么它不会受到伤害。但通常是治标不治本…… 好吧,我不认为它的公平 rexem 只是被否决了,我认为您没有解决使用 nolock 时浮出的任意错误。偶尔在您的网站上获得空白错误页面是不好的,这真的很糟糕。我不喜欢“如果你不关心脏读就很好”的断言......即使你不关心脏读也不好 当一个查询产生一个你没有实现重试逻辑的异常时你得到的空白页。当查询执行结果出现异常时,您的网站会发生什么情况,您是否到处都有重试逻辑? 采用一个非常优化的查询,您知道该查询会命中正确的索引。然后添加 nolock 提示并观察它变得更快。如果您不关心脏读,则使用 nolock 将永远不会伤害自己。【参考方案4】:

怀疑是一位有过高流量经验的“大师”...

当人们查看完全加载的页面时,网站通常是“脏的”。考虑一个从数据库加载然后保存编辑的数据的表单?人们继续认为脏读是这样的禁忌的方式是愚蠢的。

也就是说,如果您在选择的基础上构建了多个层,那么您可能会构建危险的冗余。如果您正在处理金钱或状态场景,那么您不仅需要读取/写入事务数据,还需要适当的并发解决方案(大多数“大师”不会打扰)。

另一方面,如果您对网站进行了高级产品搜索(即可能不会被缓存并且有点密集的东西)并且您曾经构建过一个拥有多个并发用户的网站(惊人的有多少“专家”没有),扼杀其背后的所有其他过程是可笑的。

了解它的含义并在适当的时候使用它。如今,您的数据库几乎一直是您的主要瓶颈,明智地使用 NOLOCK 可以为您节省数以千计的基础设施。

编辑:它不仅有助于解决僵局,还在于你要让其他人等到你完成多少,反之亦然。

Using NOLOCK Hint in EF4?

【讨论】:

【参考方案5】:

没有一个答案是错误的,但可能有点令人困惑。

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

【讨论】:

我完全不同意你的第一点。如果您正在查询单个值/行,并且您正在为该行指定唯一 id,并且您知道没有其他进程将访问它,那么使用 nolock 是完全可以接受的,并且可以减少并发应用程序中的阻塞。 不,不是。该行可能会因其他原因而更改,例如插入另一行会拆分页面。正确的索引、读取提交的快照和快照隔离几乎总是更好的主意。 @tuseau 如果你“知道”没有其他进程将访问该行,那么获取锁的行为不会阻塞任何东西,所以你(实际上)没有任何成本,【参考方案6】:

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

阅读hints and other poor ideas

什么能让你更深入地了解sql server。我通常遵循 SQL Hints 是 EVIL 的规则,但不幸的是,当我厌倦了强制 SQL Server 执行某些操作时,我会时不时地使用它们……但这些情况很少见。

卢克

【讨论】:

【参考方案7】:

当应用支持想要使用 SSMS 回答来自生产服务器的临时查询时(未通过报告处理),我要求他们使用 nolock。这样“主要”业务不受影响。

【讨论】:

【参考方案8】:

我同意一些关于 NOLOCK 提示的 cmets,尤其是那些说“在适当的时候使用它”的人。 如果应用程序写得不好并且使用了不恰当的并发方式——那可能会导致锁升级。由于它们的性质,高度事务性的表也一直被锁定。拥有良好的索引覆盖率无助于检索数据,但将 ISOLATION LEVEL 设置为 READ UNCOMMITTED 可以。 此外,我相信在许多情况下,当更改的性质是可预测的时,使用 NOLOCK 提示是安全的。例如,在制造业中,当有旅行者的工作经历不同的流程并插入大量测量时,您可以使用 NOLOCK 提示安全地对已完成的工作执行查询,这样可以避免与在表上放置 PROMOTED 或 EXCLUSIVE 锁的其他会话发生冲突/页。在这种情况下,您访问的数据是静态的,但它可能驻留在一个事务性很强的表中,每分钟有数亿条记录和数千次更新/插入。 干杯

【讨论】:

【参考方案9】:

我认为使用 nolock 几乎是不正确的。

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

如果您正在读取许多行而不是临时显示,并且关心是否能够重复结果,或者通过产生的数字进行辩护,那么 NOLOCK 是不合适的。

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

NOLOCK下可能出现的错误:

根本不返回匹配的行。 多次返回单行(包括同一主键的多个实例) 返回不匹配的行。

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

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

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

如果您因为死锁而考虑使用 NOLOCK,请检查查询计划结构是否有意外的表扫描,跟踪死锁并查看它们发生的原因。围绕写入的 NOLOCK 可能意味着先前死锁的查询都可能写入错误的答案。

【讨论】:

【参考方案10】:

如果可能的话,更好的解决方案是:

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

创建 SNAPSHOT 事务隔离级别是因为 MS 正在失去对 Oracle 的销售。 Oracle 使用撤消/重做日志来避免这个问题。 Postgres 使用 MVCC。未来 MS 的 Heckaton 将使用 MVCC,但距离生产就绪还需要数年时间。

【讨论】:

上面有一个错字。我的意思是说“更好的基本事务隔离机制”。 SNAPSHOT 事务隔离级别是 MS 的发明。基本上,它将数据放在 TEMPDB 的临时表中。该数据库在盒子上的所有数据库之间共享。因此,您需要尽可能将 SSD 用于 TEMPDB。这可能比其他选项省力。【参考方案11】:

NOLOCK 经常被用作加快数据库读取速度的神奇方式,但我尽量避免使用它。

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

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

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

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

【讨论】:

【参考方案12】:

在现实生活中,您会遇到已经编写好的系统并向表添加索引,然后会大大减慢 14gig 数据表的数据加载速度,有时您不得不在报告和月末处理中使用 WITH NOLOCK,以便聚合函数(sum、count 等)不会进行行、页、表锁定并降低整体性能。 很容易说在新系统中永远不要使用 WITH NOLOCK 并使用索引 - 但是添加索引会严重降低数据加载的等级,当我被告知时,好吧,更改代码库以删除索引,然后批量加载然后重新创建索引 - 这一切都很好,如果您正在开发一个新系统。但是,当您已经有一个系统时,就不行了。

【讨论】:

你在说什么

以上是关于NOLOCK(Sql Server 提示)是不好的做法吗?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 中WITH (NOLOCK)

[SQL SERVER][Performance]小心使用With NoLock

SQL Server中CLR表值函数(table-valued function)不能使用WITH(NOLOCK)

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

带有 (NOLOCK) 提示的 SQL 服务器

是否可以在 SQL Server 中使用 nolock 更改视图?