Tablock 提示触发死锁

Sql*_*ale 10 sql-server deadlock ssis sql-server-2008-r2 bulk-insert

我通过两个并行运行的执行 SQL 任务和以下形式的 SQL,使用最少的日志记录将两个数据集插入到一个空的堆表中。

INSERT INTO Table (TABLOCK) SELECT FROM ...
Run Code Online (Sandbox Code Playgroud)

在作业挂起一段时间后,其中一个 SQL 任务成为死锁受害者。下面是死锁图的 XML 输出。

有人能解释一下幕后发生了什么吗?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>
Run Code Online (Sandbox Code Playgroud)

事情变得更加棘手,因为我发现在大多数情况下,两个执行 SQL 任务可以成功并行运行。试试下面:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1
Run Code Online (Sandbox Code Playgroud)

由于唯一的区别是 SELECT... FROM... 语句,看起来 SELECT... FROM... 语句会对这里的锁定模式产生影响吗?

Joe*_*ish 8

数据加载性能指南是为SQL Server 2008中写,但据我可以告诉微软还没有在这方面作出了堆任何改进。这是您的加载场景的报价:

批量加载一个空的、未分区的表

将数据加载到非分区表虽然操作简单,但可以通过多种方式进行优化。

...

仅当所选批量方法在表上发出批量更新 (BU) 锁时,才可能对堆进行多个并发插入操作。两个批量更新 (BU) 锁是兼容的,因此可以同时运行两个批量操作。

在这种情况下,INSERT ... SELECT和 SELECT INTO 都有一个缺点。这两个操作都在目标上采用排他 (X) 表级锁。这意味着在给定时间只能运行一个批量加载操作,从而限制了可扩展性。但是,如果您指定 TABLOCK 提示,BCP、BULK INSERT 和集成服务都能够获取批量更新 (BU) 锁。

重要的部分是您不会获得 BU 锁INSERT ... SELECT。您将始终获得表上的排他锁,因此一次INSERT只能运行一个。

在评论中,您说您将插入 100k 行或更少,并且在插入期间其他进程不会在表上运行。当向数据库发送两个 INSERT 查询时,我希望发生以下三件事之一:

  1. 一个插入首先运行并阻止另一个插入。第二次插入等待,直到第一次插入完成。
  2. 一个插入在第二个插入开始之前完成。没有显式阻塞,但它们不会同时运行。
  3. 你会遇到一个死锁,只有一个插入成功完成。

在所有情况下,通过向TABLOCKX查询添加提示,您要么受益,要么没有受到伤害,因此这是我解决僵局的建议。如果你想知道为什么有时会发生死锁,你需要寻找另一个答案。

对于真正需要并行插入的不同场景,解决 BU 问题的两种方法是对堆进行分区并将每个会话插入到单独的分区中,或者通过 BCP、BULK INSERT 或集成服务加载数据.


Sql*_*ide 4

dbo.TargetTable您正在从两个会话插入,并且都使用TABLOCKhint.Both process9609dc8process5e13048进程保持Sch-SIX锁定,它们彼此兼容,因此两个进程可以同时保持。但两者都想将IX锁转换为Exclusive X类型。X锁彼此不兼容。因此,SQL Server选择其中一个会话作为死锁牺牲品,而不是无限地等待对方。

基本的死锁信息。

锁定兼容性(数据库引擎)图表。

检测并结束死锁。