【问题标题】:SQL Server transaction locksSQL Server 事务锁
【发布时间】:2017-09-17 00:10:50
【问题描述】:

当我使用创建事务并锁定某个表以对其执行耗时操作的 Entity Framework 执行 SQL 查询时,当另一个应用程序尝试同时从同一个表中读取时会发生什么情况?

SQL Server 会等待第一个事务完成并在锁消失时继续执行,还是会简单地返回一个错误,指出该表不可访问?

【问题讨论】:

  • 如果你想要一个排队等待的东西,你正在寻找serializable 隔离级别。 SQL Server Isolation Levels: A Series - Paul White。请注意,这不一定会锁定整个表,取决于可用的操作和索引,引擎将尝试使用键范围锁定。 Key-Range Locking - MSDN
  • 默认情况下,SQL Server 执行行级锁定,因此您几乎不会锁定整个表。如果您插入新行 - 是的,在插入行的事务提交之前,其他事务无法读取这些新行 - 但这并不意味着整个表必须被锁定允许插入(或更新或删除)几行!

标签: sql-server entity-framework


【解决方案1】:

默认情况下,EF 将创建启用 READ COMMITTED SNAPSHOT 的 SQL Server 数据库。这意味着作者不会阻止读者,读者也不会阻止作者。有关基于行版本的隔离级别的详细信息,请参阅Snapshot Isolation in SQL Server

虽然 SaveChanges() 在事务中锁定了行,但尝试读取这些行的其他会话将被重定向到版本存储,并读取这些行的最后一个正确版本。换句话说,读者在任何正在进行的交易之前的一致时间点看到每个表。

如果您对现有数据库使用 EF,它可能未配置 READ COMMITTED SNAPSHOT,在这种情况下,尝试读取被事务锁定的行的读取器将阻塞,直到事务结束。通常这只有几毫秒,除非有大量事务正在运行并且阅读器正在尝试扫描很多行,或者有运行时间更长的事务。在这种情况下,您应该在未来将数据库移动到 READ COMMITTED SNAPSHOT。

【讨论】:

    【解决方案2】:

    是的,当调用SaveChanges 并且默认隔离级别为Read Committed 时,Entity Framework 会自动创建一个事务(实际上它是从提供程序获取的,SQL Server 的默认设置是已提交读)。

    更多详情请见this documentation page

    为了更改隔离级别,您可以在实例化上下文时禁止 EF 的上下文使用它自己的事务 (contextOwnsConnection: false)。

    我首选的替代方法是使用TransactionScope,因为它不需要对实例化上下文、保存更改等的方式进行任何更改。但是,请注意its default isolation level is Serializable(引用的问题和答案也向您展示了如何改变它)

    注意:不建议使用长事务,也许您应该查看using bulk operations(或检查this library)以避免正常更改保存生成的多次插入/更新/删除。

    【讨论】:

      【解决方案3】:

      答案是,视情况而定。 SQL 可以通过多种方式锁定数据。一般来说,如果您正在进行 DML 操作(插入、更新、删除),并且您尝试从该表中访问 select,它将阻止 select,直到 DML 操作完成。

      可以使用with (nolock)set transaction isolation level read uncommmitted 覆盖(后果自负)。执行其中一项基本上可以让您的查询读取“无论数据当前是什么样子”,这可能是不完整的,甚至包含在事务完成时不存在的行。

      它唯一会返回错误的情况是您的实际连接超时(这可能是您的应用程序连接设置中的问题)或您导致表死锁(一个更复杂的主题)。

      【讨论】:

        猜你喜欢
        • 2019-06-30
        • 1970-01-01
        • 1970-01-01
        • 2016-12-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多