当我更新/插入一行数据时,是否应该锁定整个表格?

27

我有两个长时间运行的查询,都是事务型的,并且访问相同的表,但是在这些表中完全是不同的行。这些查询还根据这些查询执行一些更新和插入操作。

似乎当这些查询同时运行时,它们遇到了某种锁,这会阻止任务完成,并且在执行对其中一个行的更新时锁定。我正在读取被锁定的行时使用排他的行锁定,而显示在进程上的锁是lck_m_ix锁。

两个问题:

  1. 当我更新/插入单个行时,它是否锁定整个表?
  2. 可以采取什么措施来解决这类问题?
4个回答

24
通常不会,但这取决于具体情况(这通常是SQL Server中最常见的回答!)
在事务中涉及到数据时,SQL Server必须以某种方式锁定数据。它必须锁定表本身中的数据以及任何受影响的索引数据,同时您执行修改。为了提高并发性,服务器可能会决定使用几种“粒度”锁,以允许多个进程运行:行锁、页锁和表锁是常见的(还有更多)。采用的锁定规模取决于服务器如何执行给定的更新。复杂化的事情是,也有锁分类,如共享锁、排他锁和意向排他锁,控制被锁定对象是否可以读取和/或修改。
根据我的经验,对于对表的小部分进行更改,SQL Server主要使用页面锁,并且如果一个表的较大部分(从统计数据来看)受到更新或删除的影响,将自动升级为表锁。想法是锁定表格(一个锁)比获取和管理成千上万个单个行或页锁更快速,尤其是进行大规模更新。
要查看特定情况下发生的情况,您需要查看查询逻辑,并在运行过程中检查sys.dm_tran_locks、sys.dm_os_waiting_tasks或其他DMV中的锁定/阻塞条件。您需要发现每个过程中哪个步骤锁定了什么,以发现为什么一个进程会阻止另一个进程。

21

简短版:

  1. 不行。
  2. 修正你的代码。

详细版:

LCK_M_IX是一个意向锁,这意味着该操作将在下属元素上放置一个X锁。例如,在更新表中的某一行时,操作会在锁定正在更新/插入/删除的行之前,对表(table)进行IX锁定。意向锁是处理层次结构(如table/page/row)的常见策略,因为锁管理器无法理解要锁定的资源的物理结构(即它无法知道在页面P1上的X锁与行R1上的S锁不兼容,因为R1包含在P1中)。有关更多详细信息,请参见锁模式

如果您看到意向锁争用的事实,这意味着您正在尝试获得高级对象锁,例如表格锁。您需要分析被阻止请求的源代码(请求与LCK_M_IX不兼容的锁),并消除对象级别锁请求的原因。具体来说取决于您的源代码,我无法知道您在那里做什么。我猜测您使用了错误的锁提示。

更通用的方法是依赖快照隔离。但是,这很可能无法解决您看到的问题,因为快照隔离只能有益于行级争用问题,而不能解决请求表格锁的应用程序。


12
使用事务的一个常见目的是尽可能将其保持短小精悍。我从你问题中的措辞感受到,你正在打开一个事务,然后进行各种操作,其中一些操作需要很长时间。然后期望多个用户能够同时运行这个相同的代码。不幸的是,如果你在那组代码的开头执行了一个插入操作,然后在提交或回滚之前执行了40个其他操作,那么可能会阻塞其他所有人运行相同类型插入的操作,从而将你的操作从自由为所有变为串行。
找出每个查询正在做什么,以及是否得到了你不希望看到的锁升级。只因为你在查询中使用了WITH(ROWLOCK)并不意味着SQL Server将能够遵守...如果你涉及多个索引、索引视图、持久化计算列等,则有各种各样的原因导致你的行锁可能无效。你也可能有一些后续的事务操作花费的时间比你想象的要长,也许你没有意识到所有涉及到事务的对象的锁(不仅仅是当前运行的语句)可以持续整个事务的时间。

1
嗨,Aaron,你能详细解释一下“已持久化的计算列”何时会造成更多锁定,当然,如果它们没有被索引的话。谢谢。 - A-K
1
抱歉,Alex,请让我重新表达一下。我的意思是当您接触到已持久化的计算列,这些列也恰好被索引时。 - Aaron Bertrand

1
不同的数据库有不同的锁定机制,但像SQL Server和Oracle这样的数据库有不同类型的锁定。
SQL Server的默认设置似乎是悲观的页面锁定 - 因此,如果您只有少量记录,则所有记录都可能被锁定。
大多数数据库在运行脚本时不应该锁定,因此我想知道您是否可能在没有事务的情况下同时运行多个查询。

我总是使用锁提示,所以我明确地使用了 with (rowlock),以避免出现页锁。 - Middletone

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