选择/插入死锁


10

此实例承载SharePoint 2007数据库(SP)。我们一直在针对SP内容数据库中一个频繁使用的表遇到许多SELECT / INSERT死锁。我缩小了涉及的资源,这两个过程都需要对非聚集索引进行锁定。
INSERT在SELECT资源上需要IX锁,而SELECT在INSERT资源上需要S锁。死锁图描述了三个资源:1。SELECT中的两个(生产者/消费者并行线程)和2.)INSERT。
我附上了死锁图供您查看。因为这是Microsoft代码和表结构,所以我们无法进行任何更改。
但是,我已经在MSFT SP站点上阅读了他们建议将MAXD​​OP实例级别的配置选项设置为1。由于此实例在许多其他数据库/应用程序之间共享,因此不能禁用此设置。


因此,我决定尝试防止这些SELECT语句并行执行。我知道这不是解决方案,而是临时的修改,以帮助进行故障排除。因此,这样做后,我将“并行计算的成本阈值”从我们的标准25提高到了40,即使工作负载没有改变(SELECT / INSERT频繁发生),死锁也消失了。我的问题是为什么?

SPID 356 INSERT在属于非聚集索引的页面上具有IX锁
SPID 690 SELECT执行ID 0在属于同一非聚集索引的页面上具有S锁

现在

SPID 356希望在SPID 690资源上获得IX锁,但由于SPID 356被SPID 690执行ID 0阻止而无法获得IX锁
SPID 690执行ID 1希望在SPID 356资源上获得S锁,但由于SPID 690执行ID无法获得它SPID 356阻止了1,现在我们陷入僵局。

执行计划可以在我的SkyDrive上找到

完整的死锁详细信息可以在这里找到

如果有人可以帮助我理解为什么我会非常感激。

EventReceivers表。
id uniqueidentifier否16
名称nvarchar否512
SiteId uniqueidentifier否16
WebId uniqueidentifier否16
HostId uniqueidentifier否16
HostType int否4
ItemId int否4
DirName nvarchar否512
LeafName nvarchar否256
Type int否4
SequenceNumber int否4
汇编nvarchar否512
类nvarchar否512
数据nvarchar否512
过滤器nvarchar否512
SourceId tContentTypeId否512
SourceType int否4
Credential int否4
ContextType varbinary否16
ContextEventType varbinary否16
ContextId varbinary否16
ContextObjectId varbinary否16
ContextCollectionId varbinary否16

index_name index_description index_keys
EventReceivers_ByContextCollectionId非聚集在PRIMARY SiteId上,ContextCollectionId
EventReceivers_ByContextObjectId非聚集在PRIMARY SiteId上,ContextObjectId
EventReceivers_ById非聚集,唯一在PRIMARY SiteId
上,IRIM事件类型ContextId,ContextType,ContextEventType,SequenceNumber,Assembly,Class
EventReceivers_IdUnique非聚集,唯一,唯一键位于PRIMARY Id上


2
我们在XDL中看不到什么proc_InsertEventReceiverproc_InsertContextEventReceiver做什么?为了减少并行度,为什么不直接影响这些语句(使用MAXDOP 1),而不是对服务器范围的设置进行混淆呢?
亚伦·伯特兰

1
我很好奇您的最大MAXDOP设置是多少,以及您有多少个处理器(逻辑)。SharePoint确实能更好地工作,并且希望将其安装在MAXDOP服务器宽度为1的服务器上。我不喜欢它,但这是他们开发它的方式。您可以发布实际的执行计划吗?我在该链接上看到的只是.xdl(死锁图)
Mike Walsh 2012年

先生们,您好:非常感谢您抽出宝贵的时间在忙碌的工作中做出回应。我将在SkyDrive网站上发布程序和执行计划,以供您审核。我曾考虑过修改代码以包含查询选项MAXDOP(1),但是这样做将使我们对Microsoft的支持无效。物理服务器是ProLiant DL580 G4,MAXDOP设置为4,总共有8个物理处理器,并且禁用了H / T。
SQLJarHead 2012年

您好,先生们,我创建了一个zip包,其中包含SkyDrive上的所有详细信息。我修改了原始帖子的正文,以包含程序包的URL。请不要告诉我问题出在哪里,只需提供指导并让我为之努力。注意:我无法对基础架构进行任何代码更改或DDL修改。
SQLJarHead

1
因此,您无法更改代码,也无法更改架构,您希望我们提供什么其他解决方案?如果您担心失去Microsoft支持,那意味着您已经获得 Microsoft支持,在这种情况下-考虑到您已经告诉我们的所有限制,即无法做到-您是否考虑过与Microsoft签订支持票?
亚伦·伯特兰

Answers:


14

从表面上看,这看起来像是经典的查找死锁。此死锁模式的基本要素是:

  • 一个SELECT使用了键查找非覆盖非聚簇索引查询
  • 一个INSERT查询修改聚集索引,然后非聚集索引

SELECT访问首先访问非聚集索引,然后访问聚集索引。首先INSERT访问聚集索引,然后访问非聚集索引。当然,以不同的顺序访问相同的资源以获得不兼容的锁是一种“解决”死锁的好方法。

在这种情况下,SELECT查询为:

SELECT查询

... INSERT查询是:

INSERT查询

注意绿色突出显示的非聚集索引维护。

如果SELECT计划的并行版本与并行版本非常不同,我们将需要查看该计划的串行版本,但是正如Jonathan Kehayias在其《处理死锁指南》中指出的那样,这种特殊的死锁模式对计时和内部查询执行实现细节非常敏感。这种死锁通常是在没有明显外部原因的情况下发生的。

有了对相关系统的访问权限和适当的权限,我相信我们最终可以弄清楚为什么并行计划而不是串行计划会发生死锁(假定总体形状相同)。潜在的查询行包括检查优化的嵌套循环和/或预取-两者都可以在语句期间将隔离级别内部提升REPEATABLE READ。并行索引查找范围分配的某些功能也可能导致此问题。如果串行计划可用,我可能会花一些时间进一步研究细节,因为这可能很有趣。

对于这种类型的死锁,通常的解决方案是覆盖索引,尽管在这种情况下,列数可能不切实际(而且,有人告诉我,我们不应该在SharePoint上弄乱这类事情)。最终,使用SharePoint的仅串行计划的建议是有原因的(尽管可以说是一个不错的选择)。如果并行性成本阈值的更改目前可以解决此问题,那么这很好。从长远来看,我可能希望使用资源调控器来分离工作负载,以便SharePoint内部查询获得所需的MAXDOP 1行为,而其他应用程序能够使用并行性。

在僵局中出现的交换问题对我来说似乎是一个红鲱鱼。这仅仅是独立线程拥有资源的结果,这些资源在技术上必须出现在树中。我看不出有什么迹象表明交易所本身直接导致了僵局。


6

如果这是经典的查找死锁,则资源列表将同时包含“聚簇索引”和“非聚簇索引”。通常,SELECT将在NC索引上持有SHARED锁,并在CI上等待SHARED锁,而INSERT将在CI上获取EXCLUSIVE锁,然后在NC上等待EXCLUSIVE锁。在这种情况下,死锁xml中的资源列表将列出这两个对象。

由于死锁图仅涉及NC索引,因此我们可以排除该选项。

同样,如果由于嵌套循环连接和UNORDERED PREFETCH而导致死锁,执行计划将告诉我们是否使用UNORDERED PREFETCH算法,这里也不是这种情况(请参见下面的更新)。

因此,我们可以认为这是由于“并行计划”造成僵局。

死锁图未正确呈现,但是如果查看死锁XML,则可以看到死锁中涉及SELECT语句(SPID 690)中的两个线程。使用者线程在PAGE 1219645上拥有SHARED锁,并在端口801f8ed0(e_waitPipeGetRow)上等待生产者。生产者线程正在等待PAGE 1155940上的共享锁。

INSERT语句在PAGE 1155940上保持IX锁,并在PAGE 1219645上等待IX锁,从而导致死锁。

我相信,当对SELECT语句使用串行计划时,死锁将得到避免,因为在任何时候它都不需要SHARED锁定。我还认为串行计划将与并行计划几乎相同(没有并行运算符)。

[根据保罗的评论更新]

显然,该计划使用的是优化的嵌套循环算法

这就解释了为什么SHARED锁要保留到语句结束。与并行计划相结合的可重复读取比串行计划更容易发生死锁,因为并行计划可能从索引的不同范围获取并保留锁,而串行计划则以更顺序的方式获取锁。


同意 如果此死锁与实际的LOOKUP有关,则SELECT的等待资源将引用聚集索引。我可以通过DBCC PAGE显示每个等待资源(SPID 690等待资源= PAGE:1155940 | SPID 356等待资源= PAGE 1219645)的页眉,并且都位于索引ID 5(IndexID 5 = EventReceivers_ByContextObjectId)上,以排除此问题。指向指定表(EventReceivers)上的NC索引。
SQLJarHead 2012年

先生们,再次感谢您抽出宝贵的时间来分析这个有趣的问题。几个问题:1.)Roji指出并行SPID正在请求多个页面。我没有在任何执行计划中看到这一点。查看行数,对于INDEX SEEK运算符,两个生成器中只有一个线程正在处理任何行。您如何确定它请求多个页面?(1/2)
SQLJarHead

2.)是否将优化嵌套循环算法始终将隔离级别设置为REAPTABLE READ?我已经检查了执行计划XML输出,并且仅看到针对SPIDs连接的已提交读操作。我假设这仅在计划操作员级别被调用。(2/2)
SQLJarHead

OPTIMIZED嵌套循环的锁定行为与REPEATABLE READ(将锁保持到语句的末尾)相当,但是没有将事务的隔离级别显式设置为REPEATABLE READ。我认为这也回答了您的问题。并不是并行线程一次请求多个页面,而是一个并行线程在一页上持有锁,而另一个线程在等待另一页上锁
Roji P Thomas
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.