我们在生产环境中看到了很多这样的查询内并行线程死锁(SQL Server 2012 SP2 - 是的......我知道......),但是当查看通过扩展事件捕获的死锁 XML 时,受害者列表为空。
<victim-list />
死锁似乎在 4 个线程之间,两个与.WaitType="e_waitPipeNewRow"
和两个与WaitType="e_waitPipeGetRow"
.
<resource-list>
<exchangeEvent id="Pipe13904cb620" WaitType="e_waitPipeNewRow" nodeId="19">
<owner-list>
<owner id="process4649868" />
</owner-list>
<waiter-list>
<waiter id="process40eb498" />
</waiter-list>
</exchangeEvent>
<exchangeEvent id="Pipe30670d480" WaitType="e_waitPipeNewRow" nodeId="21">
<owner-list>
<owner id="process368ecf8" />
</owner-list>
<waiter-list>
<waiter id="process46a0cf8" />
</waiter-list>
</exchangeEvent>
<exchangeEvent id="Pipe13904cb4e0" WaitType="e_waitPipeGetRow" nodeId="19">
<owner-list>
<owner id="process40eb498" />
</owner-list>
<waiter-list>
<waiter id="process368ecf8" />
</waiter-list>
</exchangeEvent>
<exchangeEvent id="Pipe4a106e060" WaitType="e_waitPipeGetRow" nodeId="21">
<owner-list>
<owner id="process46a0cf8" />
</owner-list>
<waiter-list>
<waiter id="process4649868" />
</waiter-list>
</exchangeEvent>
</resource-list>
所以:
- 受害者名单为空
- 运行查询的应用程序不会出错并完成查询
- 就我们所见,没有明显的问题,只是图被捕获了
因此,除了噪音还有什么需要担心的吗?
如果这是通过交换溢出解决查询内并行死锁时死锁图的外观,我不会感到惊讶(因此除了性能之外没有受害者)。
您可以通过捕获交换溢出并将它们匹配(或不匹配)到死锁来证实这一理论。
将交换缓冲区写入tempdb以解决死锁并不理想。寻求消除执行计划中的顺序保留操作序列(例如,提供并行合并连接的顺序保留交换)。除非它不会导致明显的性能问题,并且您还有其他事情要担心。
碎片化,没有。过时的统计数据:在我能想到的任何具体意义上都没有,不。当然,一般来说,不具代表性的统计数据很少是一件好事。
这里的基本问题是,当线程之间的依赖关系尽可能少时,并行性效果最好。保留的顺序引入了相当讨厌的依赖关系。事情很容易搞砸,清除僵局的唯一方法是将交换中的一堆行溢出到tempdb。
笔记
在针对 SQL Server 2017 的累积更新 10 和针对 SQL Server 2016 SP2 的累积更新 2 发布的修复程序之后,当可以通过交换溢出解决死锁时,查询内并行死锁不再生成 xml 图。
为了将这些非关键的“溢出自我解决”死锁与更重要的死锁区分开来,可以将一些搜索语义应用于 Xdl 结构。
以下 SP 无法开箱即用,因为它取决于 ufn_ExtractSubstringsByPattern() 但是该方法可以替换为直接返回不同计数的方法。