向 SQL Server 插入数据会锁定整个表吗?

65

我正在使用Entity Framework,并向我们的数据库插入包含blob字段的记录。Blob字段可以达到5 MB的数据。

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

因此,如果您从表中查询任何数据,它会阻塞直到插入完成(我意识到有办法解决这个问题,但是默认情况下如何操作?)?

在发生死锁之前需要多长时间?那个时间会取决于服务器上的负载量吗?例如,如果负载不大,会需要更长的时间才能引起死锁吗?

是否有一种方法可以监视并查看任何特定时间被锁定的内容?

如果每个线程都在单个表上进行查询,那么是否存在阻塞的情况?那么死锁只会在执行跨多个表的联接查询时发生吗?

这考虑到我的大部分代码只是一堆选择语句,并没有很长时间的事务或类似的东西。

4个回答

147

哇哦,这里有很多问题啊。以下是几个答案:

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

默认情况下不会,但如果你使用TABLOCK提示或进行某些类型的批量加载操作,则会锁定整个表。

那么,如果从表中查询数据,是否会一直阻塞直到插入完成(我知道有解决方法,但默认情况下呢)?

这个问题有点棘手。如果有人试图从你锁定的表中的页面中选择数据,那么是的,你会阻塞他们。你可以通过在select语句上使用NOLOCK提示或使用已读提交快照隔离来解决这个问题。要了解隔离级别的起点,请查看Kendra Little的隔离级别海报

它需要多长时间才会导致死锁?那个时间是否取决于服务器的负载,例如,如果负载不大,将需要更长时间才会导致死锁?

死锁并不是基于时间的-它们基于依赖关系。假设我们有这种情况:

  • 查询A持有大量锁,并且为了完成他的查询,他需要被查询B锁定的东西。
  • 查询B也持有大量锁,并且为了完成他的查询,他需要被查询A锁定的东西。

两个查询都无法继续执行(类似于墨西哥僵局),因此SQL Server认为平局,终止其中一个查询,释放其锁定,并让另一个查询继续执行。SQL Server选择牺牲品是基于撤消更便宜的查询。如果你想变得高级一些,可以在特定查询上使用SET DEADLOCK_PRIORITY LOW,在它们的背后画目标,然后SQL Server将优先终止它们。

有没有一种方法可以监视并查看任何特定时间被锁定的内容?

绝对可以-有动态管理视图(DMV)可以查询,例如sys.dm_tran_locks,但最简单的方式是使用Adam Machanic免费的sp_WhoIsActive存储过程。这是一个真正漂亮的替代sp_who的工具,你可以像这样调用它:

sp_WhoIsActive @get_locks = 1

对于每个正在运行的查询,您都会得到一个小型XML,其中描述了它持有的所有锁。还有一个阻止列,因此您可以看到谁在阻止谁。要解释所持有的锁,您需要检查锁类型的Books Online描述

如果每个线程都在单个表上执行查询,那么是否有可能发生阻塞? 因此,死锁只能在对多个表进行连接并执行操作的查询中才会发生吗?

信不信由您,单个查询实际上可以产生死锁,是的,查询只能在一个表上死锁。 要了解更多关于死锁的信息,请查看Jeremiah Peschka的The Difficulty with Deadlocks


6

如果您直接控制SQL,您可以使用以下语句强制进行行级锁定:

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

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

在SQL Server中强制使用行级锁定的方法是可能的吗?

在Entity Framework中使用SELECT语句锁定表格

要查看Management Studio中当前持有的锁,请在服务器下方的"Management/Activity Monitor"中查看。它有一个按对象分类的锁定部分,因此您应该能够看到插入是否真正造成了问题。


请注意,正如我所解释的那样,在不禁用表上的锁升级的情况下,SQL Server 可能会选择忽略您的提示,并根据多种因素升级锁定。 - Thiago Dantas
我不知道关于2008年之前的行为的那一点-当我提交我的帖子时,我还没有看到你的帖子。如果可能的话,我同意避免使用大型数据块的观点。 - Andrew Bain

1

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


-1
我能提供的最佳答案是:这取决于情况。
最好的检查方式是找到您的连接 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时。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接