【问题标题】:Understanding SQL Server LOCKS on SELECT queries了解 SELECT 查询上的 SQL Server LOCKS
【发布时间】:2012-09-26 19:12:30
【问题描述】:

如果影响该表的唯一其他查询是 SELECT 查询,我想知道在表上使用 SELECT WITH (NOLOCK) 有什么好处。

SQL Server 是如何处理的? SELECT 查询会阻塞另一个 SELECT 查询吗?

我正在使用 SQL Server 2012 和 Linq-to-SQL DataContext

(编辑)

关于性能:

  • 如果使用锁定的SELECT,第二个SELECT 是否必须等待第一个SELECT 完成?
  • SELECT WITH (NOLOCK)?

【问题讨论】:

    标签: sql-server tsql linq-to-sql


    【解决方案1】:

    SQL Server 中的SELECT 将在表行上放置一个共享锁 - 而第二个SELECT 也需要一个共享锁,并且它们彼此兼容。 p>

    所以不 - 一个 SELECT 不能阻止另一个 SELECT

    WITH (NOLOCK) 查询提示的用途是能够读取正在插入(通过另一个连接)并且尚未提交的数据。

    如果没有该查询提示,SELECT 可能会被正在进行的INSERT(或UPDATE)语句阻止读取表,该语句将独占锁定在行(或可能是整个行)上表),直到该操作的事务被提交(或回滚)。

    WITH (NOLOCK) 提示的问题是:您最终可能正在读取根本不会插入的数据行(如果 INSERT 事务被回滚) - 所以您的例如报告可能会显示从未真正提交到数据库的数据。

    还有另一个可能有用的查询提示 - WITH (READPAST)。这指示SELECT 命令只跳过它尝试读取且被独占锁定的任何行。 SELECT 不会阻塞,也不会读取任何“脏”未提交的数据——但它可能会跳过一些行,例如不显示表格中的所有行。

    【讨论】:

    • 很好的答案,非常感谢!无故使用WITH (NOLOCK) 是否会产生影响(对数百个SELECT 查询)?
    • 我们在 99.5% 的选择中使用 with nolock,这不是开玩笑。如果管理员正在更新用户记录,您不希望这导致报告坐在那里并等待整个分布式事务完成。所以他们的旧数据显示在报告中。谁在乎?如果该报告在前一秒运行,那么与行锁相同的数据就会出现。唯一值得关注的地方是尚未提交的数据。如果您显示“最近一小时内的订单”,这可能是一个问题,但与速度/并发增益相比,这是一个很小很小的问题。
    • 此外,由于“报告”作为示例被丢弃,因此报告的时间段通常不是过去 5 分钟。使用 nolock 报告上个月的数据 - 数据不会在一个月后回滚。
    • @FrancisP:如果您插入少量行,则不会 - 在这种情况下,它只会锁定正在插入的新行。如果您一次插入大约 5000 多行 - 则会发生锁升级并且整个表将被独占锁定。
    • 非常好的答案..感觉就像 SQL 锁的一体化教程!很高兴我来到这里!
    【解决方案2】:

    在性能方面,您始终专注于选择。
    Shared 不会阻止读取。
    共享锁块更新。
    如果您有数百个共享锁,则更新需要一段时间才能获得独占锁,因为它必须等待共享锁清除。

    默认情况下,选择(读取)使用共享锁。
    共享 (S) 锁允许并发事务读取 (SELECT) 资源。
    共享锁对其他选择(1 或 1000)没有影响。

    区别在于 nolock 与 shared lock 如何影响更新或插入操作。

    当资源上存在共享 (S) 锁时,没有其他事务可以修改数据。

    共享锁会阻止更新!
    但是 nolock 不会阻止更新。

    这会对更新的性能产生巨大影响。它也会影响插入。

    脏读(nolock)听起来很脏。你永远不会得到部分数据。如果更新将 John 更改为 Sally,您将永远无法获得 Jolly。

    我经常使用共享锁来实现并发性。数据一读就过时了。下一毫秒更改为 Sally 的 John 的读取是陈旧数据。在下一毫秒回滚 John 的 Sally 读取是陈旧数据。那是在毫秒级别。如果用户使用共享锁,我有一个数据加载器需要 20 小时才能运行,而如果用户没有使用锁,则需要 4 小时才能运行。在这种情况下,共享锁会导致数据过期 16 小时。

    不要错误地使用 nolocks。但他们确实有一席之地。如果您要在一个字节设置为 1 时取消支票,然后在支票被取消时将其设置为 2 - 不是不加锁的时间。

    【讨论】:

    • 谢谢。我们看到了类似的性能特征。如果我们需要锁定来进行读取,我们的网站将无法运行,并且在大多数情况下没有它的影响是微不足道的。
    • @BrianWhite 谢谢。有人得到它。而且我在更新和插入时使用了很多表锁。进入,完成,然后退出是我的方法。
    • 脏读(nolock)听起来很脏。你永远不会得到部分数据。如果更新将 John 更改为 Sally,您将永远无法获得 Jolly。 - 我们读约翰的吧?
    • sql server 中的更新使用更新锁 (U),后来转换为排他锁 (X)。 (参见madeiradata.com/role-update-lock-sql-server)更新锁不会阻塞共享锁,但排他锁会阻塞所有其他锁(参见msdn.microsoft.com/en-us/library/ms186396(v=sql.105).aspx)。
    • @kolobok 需要更新一段时间才能获得独占锁
    【解决方案3】:

    我必须添加一条重要的评论。大家都在说NOLOCK只读取脏数据。这并不精确。您也可能会两次获得同一行,或者在阅读期间跳过整行。原因是你可以在 SQL Server 重新平衡 b-tree 的同时请求一些数据。

    检查其他线程

    https://stackoverflow.com/a/5469238/2108874

    http://www.sqlmag.com/article/sql-server/quaere-verum-clustered-index-scans-part-iii.aspx)

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

    【讨论】:

      【解决方案4】:

      在我的工作中,我们有一个非常大的系统,可以同时在多台 PC 上运行,其中包含数十万行甚至数百万行的非常大的表。

      当您在一个非常大的表上进行 SELECT 时,假设您想知道用户在过去 10 年中所做的每笔交易,并且该表的主键没有以有效的方式构建,查询可能运行几分钟。

      然后,我们的应用程序可能会同时在许多用户的 PC 上运行,访问同一个数据库。因此,如果有人试图插入另一个 SELECT 正在读取的表中(在 SQL 试图读取的页面中),则可能会发生 LOCK 并且两个事务相互阻塞。

      我们必须在我们的 SELECT 语句中添加一个“NO LOCK”,因为它是一个巨大的 SELECT 表,同时被很多用户大量使用,而且我们一直都有 LOCKS。

      不知道我的例子够不够清楚?这是一个真实的例子。

      【讨论】:

      • 谢谢你的例子,但我只是想知道 SELECT 查询会影响其他 SELECT 查询(在同一张表上)..
      • 他们不会,但是选择语句可能是包含更新的事务的一部分。更新 tbl set x = (select max(y) from tbl) 其中 z = (select min(a) from tbl)。如果你有一个并发 select z from tbl well 其他选择不会阻止它,但更新是。
      • 我确实遇到了这个问题,即长时间运行的选择阻止了我的插入
      • 事务不会互相阻塞——选择会阻塞更新。这里有几个有趣的链接帮助我更多地了解这些东西是如何工作的:first onesecond one
      • @JonnyLeeds :您的第二个链接不再起作用。这是SQL Server: Locking basics的存档链接
      【解决方案5】:

      SELECT WITH (NOLOCK) 允许读取未提交的数据,这相当于在您的数据库上设置了READ UNCOMMITTED 隔离级别。与在整个数据库上设置隔离级别相比,NOLOCK 关键字允许更细粒度的控制。

      维基百科有一篇有用的文章:Wikipedia: Isolation (database systems)

      在其他 stackoverflow 文章中也有详细讨论。

      【讨论】:

      • 感谢 rghome 提供的额外信息。
      • 这就是为什么我更喜欢使用READUNCOMMITTEDNOLOCK 的别名)提示的原因,when 这是一个有效的用例。这样做会使实际操作(不是真正“没有锁”)变得不那么清晰。
      【解决方案6】:

      select with no lock - 将选择可能/可能不会插入的记录。你会读到一个脏数据。

      例如 - 假设一个事务插入 1000 行然后失败。

      当您选择时 - 您将获得 1000 行。

      【讨论】:

      • 但是如果不打算在该表中插入记录怎么办,NO LOCK 是否仍然相关?
      • 不,不是。因为 read 使用了一个共享锁,它可以被多个会话获取。无法获取脏数据。
      • 我宁愿不回答我不确定的事情。 :-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-08
      • 1970-01-01
      • 2013-11-21
      • 2021-12-05
      相关资源
      最近更新 更多