什么时候适合使用 NOLOCK?

Posted

技术标签:

【中文标题】什么时候适合使用 NOLOCK?【英文标题】:When is it appropriate to use NOLOCK? 【发布时间】:2009-05-03 09:14:07 【问题描述】:

由于一些长时间运行的查询,我不时遇到超时问题和死锁。

我想知道什么时候最适合使用 NOLOCK 以及在哪里使用?

我是否在更新和插入中使用它?还是阅读?

【问题讨论】:

【参考方案1】:

请注意,您可以为每个表指定 nolock。

我通常在复杂的 SELECT 查询中使用 nolock,但仅用于几乎从未更改过的小查找表,以及仅用于显示的数据。您知道列出当前半年价格的表格,或将 id 查找到字符串等。这些内容只会随着重大更新而改变,之后服务器通常会定期重启。

这显着提高了性能,在最繁忙的时候减少了死锁的机会,更重要的是,在最坏的情况下,对于涉及大量表的查询(这是合乎逻辑的,他们必须获得更少的锁,并且这些sidetables通常几乎无处不在,通常从需要锁定的7-8个表减少到4个)

但是添加它要非常小心,不要急于求成,也不要例行公事。用得好不会疼,用得不好会疼得厉害。

不要将它用于高度关键的东西、计算的东西等,因为它会变得不一致,任何迟早会导致写入的东西。

另一个这样的优化是 ROWLOCK,它只锁定在行级别。这在更新(或删除)行彼此不相关的表时非常有用,例如只放入日志记录的表(并且插入的顺序无关紧要)。如果您有一个方案,在事务结束时将日志记录写入某个表,这也可以大大加快速度。

如果您的数据库的写入百分比相对较低,则可能不值得。我的读写比率低于 2:1。

我在处理此问题时保存的一些 URL:

http://www.developerfusion.com/article/1688/sql-server-locks/4/

【讨论】:

关于读取提交级别的不经常更新的表,SQL Server 可以跳过对没有未提交更改的页面进行共享行锁定。在我看来,NOLOCK 在数据正在被更新并且您正在争用的情况下,会提供您提到的好处(更快的性能,更少的死锁)。 有点晚了,但这不是我所看到的。这些实际上是只读的,更新之间有几个月的时间。我做了一些研究,IIRC 它与行锁(读取?)在某些可能会阻碍其他优化的情况下太快地更新表锁有关?但也可能只是 sqlserver 太旧了。(MSSQL2005 和更早的一个版本)【参考方案2】:

SQL Server 中有四个transaction isolation levels:

    阅读未提交 已提交阅读 可重复读取 可序列化

对于它所应用的表,NOLOCK 相当于“读取未提交”。这意味着您可以看到未来可能回滚的事务中的行,以及许多其他奇怪的结果。

不过,nolock 在实践中效果很好。尤其是对于显示稍有错误的数据并不是世界末日的只读查询,例如业务报告。我会避免它靠近更新或插入,或者通常靠近决策代码的任何地方,特别是如果它涉及发票。

作为 nolock 的替代方案,请考虑“已提交的读取快照”,它适用于具有大量读取和较少写入活动的数据库。您可以通过以下方式打开它:

ALTER DATABASE YourDb SET READ_COMMITTED_SNAPSHOT ON;

它适用于 SQL Server 2005 及更高版本。这就是 Oracle 默认的工作方式,也是 *** 本身使用的方式。甚至还有一个关于它的 coding horror 博客条目。

附:长时间运行的查询和死锁也可能表明 SQL Server 正在使用错误的假设。检查您的统计信息或索引是否过时:

SELECT 
    object_name = Object_Name(ind.object_id),
    IndexName = ind.name,
    StatisticsDate = STATS_DATE(ind.object_id, ind.index_id)
FROM SYS.INDEXES ind
order by STATS_DATE(ind.object_id, ind.index_id) desc

应在每周维护计划中更新统计信息。

【讨论】:

【参考方案3】:

使用 nolock 作为最后的手段。大多数死锁问题可以通过调整查询和/或调整索引来解决。我想我在过去 5 年中看到了一个无法通过调整两者之一来解决的僵局。

另请注意,NOLOCK 仅在 select 语句上受到尊重。数据修改将始终锁定,该行为无法更改。因此,如果您遇到了 writer/writer 死锁(很常见),那么没有锁根本无济于事。

另外请注意,除了返回脏数据之外,nolock 还可能导致重复行(从基础表读取两次的行)和缺失行(基础表中根本没有读取的行)。

Nolock 本质上对 SQL Server 意味着“我不介意我的结果是否稍微不准确”

快照隔离是一种选择。只要确保您首先仔细测试,因为 TempDB 上增加的负载可能非常严重,这取决于您的事务的频率和持续时间。另请注意,虽然您不会在快照隔离中看到死锁,但您可能会遇到更新冲突。再次测试并确保您的应用程序正常运行并且可以处理它们遇到的任何错误。

【讨论】:

【参考方案4】:

在可以接受脏读和虚拟记录时使用它,例如

【讨论】:

交易失败的后果也很糟糕。如果您学习 nolock,那么您一开始就处于不理想的状态。【参考方案5】:

当可以读取脏数据时,您应该使用 nolock。可能对数据库进行大量更改的大型事务可能仍在进行中,使用 nolock 只会返回它到目前为止设置的数据。如果该事务然后回滚您正在查看的数据可能是错误的。因此,您应该只在返回的结果可能是错误的情况下才使用它。

死锁是一个常见问题,但 10 次中有 9 次完全是由开发人员问题引起的。我会专注于找出死锁的原因,而不是使用 nolock。很可能只是一个事务以与所有其他事务不同的顺序做事。仅仅解决这个问题可能会使您的所有问题都消失。

【讨论】:

您能否详细说明“很可能只是一个事务以与所有其他事务不同的顺序执行操作。仅修复一个事务可能会使您的所有问题都消失。”?这会很有帮助。【参考方案6】:

对于没有读锁的事务一致视图,建议在 SQL Server 中启用快照隔离。

这与 NOLOCK 略有不同,因为当您读取信息时,结果始终反映已提交数据的版本,而不是查看未提交数据的可能性。这提供了与 NOLOCK(没有“读取”锁)相同的锁定并发性,并且结果更清晰。

人们应始终牢记,即使事务保持一致,您在显示时继续显示或使用的数据也可能是错误的或过时的。我见过太多人认为如果他们使用数据的速度足够快,或者如果他们在查询/事务中使用它就可以了。这很荒谬——我认为,可重复的一致性级别一开始就不应该实施,因为它只会鼓励不良行为。它们在 Oracle 中不存在。

就我个人而言,我喜欢为某些非关键数据视图和报告禁用锁定,因为它给系统带来的负载更少,而且提供稍微不准确的结果的可能性很小。

利用可重复的读取一致性级别并犯下诸如为用户输入保留开放事务之类的罪行,在初始开发方面对开发人员来说可能会更容易一些,但几乎总是会导致实现任何合理希望的主要道路“障碍”扩展您的应用程序。

我的观点是,最好的方法始终是“仔细检查”仍然必须为真的条件,以便将更新应用于任何数据。

不好:

UPDATE myaccount SET balance = 2000

更好:

UPDATE myaccount SET balance = balance + 2000

更好:

UPDATE myaccount SET balance = 2000 WHERE balance = 0 AND accountstatus = 1

最后,应用程序必须检查行数以确保在向用户提供成功反馈之前实际更新了预期的行数。

【讨论】:

以上是关于什么时候适合使用 NOLOCK?的主要内容,如果未能解决你的问题,请参考以下文章

sqlserver with(nolock)

使用 WITH(NOLOCK) 提高性能

使用 NOLOCK 读取单个静态行。有啥害处?

在数据仓库场景中使用 WITH(NOLOCK) 有啥缺点吗

sql中nolock的语法

PostgreSQL 等效于 SQLServer 的 NoLock 提示