SQL*_*ead 11 sql-server deadlock sharepoint
此实例承载 SharePoint 2007 数据库 (SP)。针对 SP 内容数据库中一个使用率很高的表,我们遇到了许多 SELECT/INSERT 死锁。我已经缩小了涉及的资源范围,两个进程都需要对非聚集索引进行锁定。
INSERT 需要对 SELECT 资源的 IX 锁,而 SELECT 需要对 INSERT 资源的 S 锁。死锁图描绘了三个资源,1.) 两个来自 SELECT(生产者/消费者并行线程),和 2.) INSERT。
我已附上死锁图供您查看。因为这是 Microsoft 代码和表结构,我们无法进行任何更改。
但是,我在 MSFT SP 站点上读到,他们建议将 MAXDOP 实例级别配置选项设置为 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 S 锁
SPID 690 执行 ID 1 想要 SPID 356 资源上的 S 锁,但无法获得它,因为 SPID 690 执行 ID 1 被 SPID 356 阻止,现在我们陷入了僵局。
如果有人能帮助我理解为什么我会非常感激。
事件接收器表。
ID唯一标识符无16
名称nvarchar的无512
SITEID唯一标识符没有16
WebId唯一标识符没有16
HOSTID唯一标识符没有16
HOSTTYPE INT无4
项目Id INT无4
DIRNAME nvarchar的无512
LeafName nvarchar的无256
int类型没有4
的SequenceNumber INT无4
装配nvarchar的无512
类nvarchar的no 512
Data nvarchar no 512
Filter nvarchar no 512
SourceId tContentTypeId no 512
SourceType int no 4
Credential int no 4
ContextType varbinary no 16
ContextEventType varbinary no 16
ContextId varbinary no 16
ContextObjectId varbinary no 16
ContextCollectionId varbinary no 16
index_name index_description index_keys EventReceivers_ByContextCollectionId
nonclustered 位于PRIMARY SiteId,ContextCollectionId
EventReceivers_ByContextObjectId nonclustered 位于PRIMARY SiteId,ContextObjectId
EventReceivers_ById nonclustered,唯一位于PRIMARY SiteId,Id
EventReceivers_ByContextCollectionId nonclustered,SiteCollectionId 上,唯一,WebObjectId ContextId、ContextType、ContextEventType、SequenceNumber、Assembly、Class
EventReceivers_IdUnique 非聚集、唯一、唯一键位于 PRIMARY Id 上
Pau*_*ite 14
从表面上看,这看起来像是一个经典的查找死锁。这种死锁模式的基本要素是:
SELECT使用了键查找非覆盖非聚簇索引查询INSERT查询修改聚集索引,然后非聚集索引首先SELECT访问非聚集索引,然后访问聚集索引。首先INSERT访问聚集索引,然后访问非聚集索引。以不同的顺序访问相同的资源获取不兼容的锁当然是“实现”死锁的好方法。
在这种情况下,SELECT查询是:

...INSERT查询是:

请注意绿色突出显示的非聚集索引维护。
我们需要查看SELECT计划的串行版本,以防它与并行版本非常不同,但正如 Jonathan Kehayias 在他的处理死锁指南中指出的那样,这种特殊的死锁模式对时间和内部查询执行实现细节非常敏感。这种类型的僵局经常在没有明显外部原因的情况下出现和消失。
鉴于对相关系统的访问权和适当的权限,我确信我们最终可以确切地找出为什么并行计划而不是串行计划发生死锁(假设总体形状相同)。潜在的查询行包括检查优化的嵌套循环和/或预取——这两者都可以在语句的持续时间内在内部将隔离级别升级到REPEATABLE READ。也有可能是并行索引搜索范围分配的某些特性导致了这个问题。如果串行计划可用,我可能会花一些时间进一步研究细节,因为它可能很有趣。
这种类型的死锁的通常解决方案是使索引覆盖,尽管在这种情况下的列数可能使这变得不切实际(此外,我们不应该在 SharePoint 上弄乱这些东西,我被告知)。最终,在使用 SharePoint 时推荐仅串行计划是有原因的(虽然不一定是好的,但归根结底时)。如果并行性成本阈值的变化暂时解决了这个问题,那就太好了。从长远来看,我可能会考虑分离工作负载,也许使用资源调控器,以便 SharePoint 内部查询获得所需的MAXDOP 1行为,并且其他应用程序能够使用并行性。
僵局痕迹中出现的交流问题对我来说似乎是一种红鲱鱼;仅仅是拥有资源的独立线程的结果,这些资源在技术上必须出现在树中。我看不出任何迹象表明交易所本身正在直接导致僵局问题。
如果这是典型的查找死锁,则资源列表将包括聚集索引和非聚集索引。通常,SELECT 将持有 NC 索引上的 SHARED 锁并等待 CI 上的 SHARED 锁,同时 INSERT 将获取 CI 上的 EXCLUSIVE 锁并等待 NC 上的 EXCLUSIVE 锁。在这种情况下,死锁 xml 中的资源列表将列出这两个对象。
由于死锁图只涉及 NC 索引,我们可以排除该选项。
此外,如果这是由于Nested Loop Join with UNORDERED PREFETCH导致的死锁,执行计划将告诉我们是否使用了 UNORDERED PREFETCH 算法,这里又不是这种情况(请参阅下面的更新)。
死锁图未正确呈现,但如果您查看 Deadlock XML,您可以看到 SELECT 语句 (SPID 690) 中的两个线程参与了死锁。消费者线程在 PAGE 1219645 上持有一个共享锁,并在 port801f8ed0 (e_waitPipeGetRow) 上等待生产者。生产者线程正在等待 PAGE 1155940 上的共享锁。
INSERT 语句在 PAGE 1155940 上持有 IX 锁并在 PAGE 1219645 上等待 IX 锁,从而导致死锁。
我相信在对 SELECT 语句使用串行计划时会避免死锁,因为它在任何时候都不需要在多于一页上使用共享锁。我还认为串行计划将与并行计划几乎相同(没有并行操作符)。
[根据保罗的评论更新]
显然该计划正在使用优化的嵌套循环算法
这就解释了为什么 SHARED 锁一直保持到语句结束。REPEATABLE READ 结合并行计划比串行计划更容易出现死锁,因为并行计划可能从索引的不同范围获取和保留锁,而串行计划以更顺序的方式获取锁。