【问题标题】:Does inserting data into SQL Server lock the whole table?将数据插入 SQL Server 是否会锁定整个表?
【发布时间】:2011-09-13 01:40:47
【问题描述】:

我正在使用Entity Framework,我正在将记录插入到我们的数据库中,其中包括一个 blob 字段。 Blob 字段最多可以包含 5 MB 的数据。

向这个表中插入一条记录时,是否会锁定整个表?

所以如果你从表中查询任何数据,它会阻塞直到插入完成(我知道有办法解决这个问题,但我是默认说话)?

需要多长时间才会导致死锁?该时间是否取决于服务器上的负载量,例如如果负载不多,会不会导致死锁时间更长?

有没有办法在任何特定时间监控和查看锁定的内容?

如果每个线程都在对单个表进行查询,那么是否存在可能发生阻塞的情况?那么,不是只有当您的查询具有连接并作用于多个表时才会发生死锁吗?

这是考虑到我的大部分代码只是一堆 select 语句,而不是一堆长时间运行的事务或类似的东西。

【问题讨论】:

    标签: sql-server entity-framework


    【解决方案1】:

    天哪,你这里有很多问题,呵呵。以下是一些答案:

    向该表中插入记录时,是否会锁定整个表?

    默认情况下不是,但如果您使用 TABLOCK 提示或者如果您正在执行某些类型的批量加载操作,那么可以。

    所以如果你从表中查询任何数据,它会阻塞直到插入完成(我知道有办法解决这个问题,但我是默认说话)?

    这个有点棘手。如果有人试图从您已锁定的表中的页面中选择数据,那么是的,您将阻止他们。您可以使用诸如选择语句上的 NOLOCK 提示或使用已提交的读取快照隔离之类的方法来解决此问题。有关隔离级别如何工作的起点,请查看Kendra Little's isolation levels poster

    需要多长时间才会导致死锁?该时间是否取决于服务器上的负载量,例如如果负载不多,是否会导致死锁更长时间?

    死锁不是基于时间的,而是基于依赖关系的。假设我们遇到了这种情况:

    • 查询 A 持有一堆锁,为了完成他的查询,他需要被查询 B 锁定的东西
    • 查询 B 也持有一堆锁,为了完成他的查询,他需要被查询 A 锁定的东西

    这两个查询都不能向前推进(想想墨西哥的僵局),因此 SQL Server 称之为平局,将某人的查询打到后面,释放他的锁,并让另一个查询继续进行。 SQL Server 根据回滚成本较低的受害者来选择受害者。如果你想变得花哨,你可以在特定的查询上使用 SET DEADLOCK_PRIORITY LOW 来绘制目标的背面,SQL Server 会先射击它们。

    有没有办法在任何特定时间监控和查看锁定的内容?

    当然 - 您可以像 sys.dm_tran_locks 一样查询动态管理视图 (DMV),但最简单的方法是使用 Adam Machanic's free sp_WhoIsActive stored proc。它是 sp_who 的一个非常巧妙的替代品,您可以这样称呼它:

    sp_WhoIsActive @get_locks = 1
    

    对于每个正在运行的查询,您将获得一个描述它持有的所有锁的小 XML。还有一个阻止列,因此您可以查看谁在阻止谁。要解释持有的锁,您需要检查Books Online descriptions of lock types

    如果每个线程都在对单个表进行查询,那么是否存在可能发生阻塞的情况?那么,不是只有当您的查询具有连接并作用于多个表时才会发生死锁吗?

    信不信由你,a single query can actually deadlock itself,是的,查询可能只在一张表上死锁。要了解更多关于死锁的信息,请查看The Difficulty with Deadlocks by Jeremiah Peschka

    【讨论】:

      【解决方案2】:

      如果您可以直接控制 SQL,则可以使用以下命令强制行级锁定:

      INSERT INTO WITH (ROWLOCK) MyTable(Id, BigColumn) 
      VALUES(...)
      

      这两个答案可能会有所帮助:

      Is it possible to force row level locking in SQL Server?

      Locking a table with a select in Entity Framework

      要查看 Management Studio 中当前持有的锁,请查看服务器下方,然后查看管理/活动监视器。它有一个按对象锁定的部分,因此您应该能够查看插入是否真的导致了问题。

      【讨论】:

      • 请注意,正如我所解释的,在不禁用表上的锁升级的情况下,SQL Server 可以选择忽略您的提示并根据几个因素升级锁。
      • 我不知道关于 2008 年之前的行为的这一点 - 我提交我的帖子时没有看到你的帖子。我同意尽可能避免大斑点的观点。
      【解决方案3】:

      死锁错误通常会很快返回。等待锁定时发生超时错误不会导致死锁状态。 SQL Server 通过查找锁请求中的循环来检测死锁。

      【讨论】:

        【解决方案4】:

        我能想到的最佳答案是:视情况而定。

        最好的检查方法是找到您的连接 SPID 并使用 sp_lock SPID 检查 TAB 类型上的锁定模式是否为 X。您还可以使用SELECT OBJECT_NAME(objid) 验证表名。我也喜欢使用下面的查询来检查锁定。

            SELECT RESOURCE_TYPE,RESOURCE_SUBTYPE,DB_NAME(RESOURCE_DATABASE_ID) AS 'DATABASE',resource_database_id DBID,
            RESOURCE_DESCRIPTION,RESOURCE_ASSOCIATED_ENTITY_ID,REQUEST_MODE,REQUEST_SESSION_ID,
            CASE WHEN RESOURCE_TYPE = 'OBJECT' THEN OBJECT_NAME(RESOURCE_ASSOCIATED_ENTITY_ID,RESOURCE_DATABASE_ID) ELSE '' END OBJETO
            FROM SYS.DM_TRAN_LOCKS (NOLOCK)
            WHERE REQUEST_SESSION_ID = --SPID here
        

        在 SQL Server 2008(及更高版本)中,您可以禁用表上的锁升级并在插入子句中强制执行 WITH (ROWLOCK),从而有效地强制执行行锁。在 SQL Server 2008 之前无法做到这一点(您可以编写 WITH ROWLOCK,但 SQL Server 可以选择忽略它)。

        我在这里说的是将军,我对 BLOB 没有太多经验,因为我通常建议开发人员避免使用它们,尤其是在大于 1 MB 的情况下。

        【讨论】:

          猜你喜欢
          • 2010-12-04
          • 1970-01-01
          • 2014-09-01
          • 2021-01-28
          • 2015-08-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多