Tablock提示触发死锁


10

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

INSERT INTO Table (TABLOCK) SELECT FROM ...

作业暂停后,其中一个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>

事情变得棘手得多,因为我发现在大多数情况下,两个Execute SQL Tasks可以成功并行运行。请尝试以下方法:

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

由于唯一的区别是SELECT ... FROM ...语句,看起来SELECT ... FROM ...语句可能对这里的锁定模式有影响?


您可以指定TABLOCKX而不是TABLOCK来防止死锁。尽管这也会序列化对表的访问,但是您仍将获得最少的日志记录。
Dan Guzman

Answers:


8

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

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

通过简单的操作,可以通过几种方式优化将数据加载到非分区表中的过程。

...

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

在这种情况下,INSERT…SELECT和SELECT INTO都有缺点。这两个操作都在目标上进行独占(X)表级锁定。这意味着在给定的时间只能运行一个批量加载操作,从而限制了可伸缩性。但是,如果指定了TABLOCK提示,则BCP,BULK INSERT和Integration Services都能够获取批量更新(BU)锁。

重要的是您不会使用来获得BU锁定INSERT ... SELECT。您将始终在表上获得排它锁,因此一次INSERT只能运行一个。

在注释中,您说您将插入10万行或更少的行,并且在插入期间其他进程将不会在表上运行。当向数据库发送两个INSERT查询时,我希望发生以下三种情况之一:

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

在所有情况下TABLOCKX,向查询添加提示都会使您受益或不受影响,因此,我建议您解决僵局。如果您想知道为什么有时会发生死锁,则需要寻找另一个答案。

对于在真正需要并行插入的不同情况下,解决BU问题的两种方法是对堆进行分区并将每个会话插入单独的分区中,或者通过BCP,BULK INSERT或Integration Services加载数据。 。


感谢您的回答,但是我目前遇到的唯一情况是遇到僵局。在大多数情况下,当您并行发出INSERT INTO WITH(TABLOCK)SELECT FROM时,作业不会失败。顺便说一句,我正在使用SQL SERVER 2008 R2。我在问题中添加了一个成功的例子。
SqlWhale '17

@tec在没有失败的情况下,可能实际上最终以串行方式运行。也许您只有在一定的启动时间(例如计划编制)时才遇到问题。
马丁·史密斯

@MartinSmith我在项目上遇到问题的查询比SELECT 1示例要复杂得多,它需要更多的编译开销,但它只是从几个表中读取。我试图通过更复杂的查询来重现这种类型的死锁。
SqlWhale

@TecKnowNothing关于您插入多少行?该流程每天运行几次?加载数据时,其他查询是否从表中进行选择?
Joe Obbish

@JoeObbish 1.我认为这里的行数不是问题,每个查询只有4000-70000,但是它们是多维数据集的高度聚合数据。2.它仍处于测试阶段,应该每天运行一次。3.什么都没有从目标表中读取。
SqlWhale

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.