【问题标题】:Best alternative to WITH(NOLOCK)?WITH(NOLOCK) 的最佳替代方案?
【发布时间】:2015-10-05 14:07:09
【问题描述】:

我在 C#.Net 中创建一个自我项目,通过使用 3 层应用程序,我不想让我的 sql 语句具有事务性,这些语句用于多个存储过程,我一直在使用 WITH (NOLOCK) 方法来查询那些在事务期间用于插入或更新的表,在 C# 端我一直在使用 TransactionScope,但我最近读到不推荐使用 WITH(NOLOCK),因为它可以导致幻读或使用脏数据和不一致数据。我的问题是,为了使用在事务期间插入或更新的数据,当涉及到选择时,在事务操作方面最好的方法是什么?无论是数据库端还是业务代码端。

【问题讨论】:

  • 我认为如果您想要全面的答案,最好在 DBA 社区中询问这个问题。 dba.stackexchange.com
  • 最好的“替代方案”就是将其删除。事务中的任何数据移动在该事务中仍然可见。
  • 如果无等待读取对您很重要,请考虑使用快照隔离;但它有自己的蠕虫袋,所以要小心先研究它。

标签: .net sql-server transactions nolock


【解决方案1】:

您是否考虑过快照隔离?它提供了完美的读取一致性,并且完全不锁定数据。

SI 在许多 RDBMS 上是标准的,并且默认启用。不知道为什么 SQL Server 的人对使用它犹豫不决。缺点是轻微的,但您需要研究它们。

【讨论】:

  • 如果你能提供一些缺点就好了,因为你提到了它。
【解决方案2】:

你根本不需要它。如果你修改了一些数据,然后在同一个事务中查询它,你会得到修改后的数据。

您的事务将对修改后的数据持有排他锁,因此它可以对它做任何事情 - 查询、再次修改等。

如果指定 WITH(NOLOCK),则允许查询忽略来自其他事务的排他锁。这将导致您的查询返回不正确的数据。

【讨论】:

    【解决方案3】:

    也许你不知道你想要什么。

    WITH(NOLOCK) 返回脏数据和不一致数据。

    如果您想要干净的数据,则必须删除此子句,并等待您要读取的表的最终并发更新。

    【讨论】:

      【解决方案4】:

      链接:https://docs.microsoft.com/en-us/sql/connect/jdbc/understanding-isolation-levels?view=sql-server-ver15#remarks

      下表显示了不同隔离级别所允许的并发副作用。

      阅读上面的链接,了解每个列标题的含义。

      大多数数据库的默认设置为已提交读。因此,当您在使用或不使用 NOLOCK 的情况下触发查询时,并假设您的数据库处于默认的读取提交模式,您将面临不可重复读取和 Phantoms 的问题。

      当您使用 NOLOCK 时,您还面临脏读问题。这意味着您甚至可以在事务提交或回滚之前读取它们所做的修改。

      还有缺失行的问题(例如假设您的选择查询已读取更新的行)和重复行(例如假设已读取的行已更新为未更新的值)由选择查询读取)。 NOLOCK 会导致分配顺序扫描而不是索引顺序扫描,这会导致丢失或重复行问题。

      您需要务实地思考上述问题对您的影响,并决定是否使用 NOLOCK 提示。

      这里是一个例子:https://dba.stackexchange.com/questions/306994/what-are-the-problems-with-using-nolock-given-an-approach-to-handle-the-short-co

      以下答案给出了一个关于读取某些行两次的示例:https://stackoverflow.com/a/2268078/1779091(答案粘贴在下面)

      NOLOCK 提示会导致脏读异常,其中一个异常是 重复阅读。如果更新更改了 该行在查询扫描的索引中的位置:

      假设您在表中有 2 行,具有 ID 键,具有键值 1 和 2 的行 一个请求 (T1) 运行 UPDATE table SET key=3 WHERE key=1;

      第二个请求 (T2) 运行 SELECT ... FROM table WITH(NOLOCK);

      T1 锁定键值为 1 的行

      T2 忽略 T1 拥有的锁并读取键值为 1 的行

      T2 继续读取键值为 2 的行

      T1 更新 该行,并且该行在索引中移动到键的新位置 值 3

      T2继续扫描,读取键值为3的行

      所以 SELECT 读取了一行两次,一次是在它的键值为 1 和 有一次,它有一个键值 3。这只是一个简单的例子 会发生什么。实际上,更复杂的查询可以运行复杂 计划和使用其他索引,都受到此类异常的影响。

      简而言之:NOLOCK 提示是邪恶的。如果要避免争用,请使用 快照隔离。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-11
        • 1970-01-01
        • 2014-02-02
        • 1970-01-01
        • 2021-01-17
        • 1970-01-01
        相关资源
        最近更新 更多