Mik*_*keb 6 performance foreign-key sql-server delete sql-server-2008-r2 performance-tuning
我有一个需要很长时间的删除查询。查看执行计划,我看到删除查询中的大部分估计成本都位于数据模型的一部分中,该部分具有大量数据(例如 400k 行),这看起来不错,但我不明白一件事.
数据模型的精简视图:
table ParentObject
int parentObjectId (PK)
table Child
int childId (PK)
int parentId (FK)
<stuff>
table GrandChild
int grandChildId (PK)
int childId (FK)
<more stuff>
Run Code Online (Sandbox Code Playgroud)
其中父对象可能有 200,000 个子对象,而子对象有 2 个左右的 GrandChildren。我对调整以下性能感兴趣:
DELETE FROM ParentObject WHERE parentObjectId = %d;
Run Code Online (Sandbox Code Playgroud)
在 Grandchild 上,(childId, + 两个其他列) 以及主键索引上有一个额外的非聚集索引。在 child 上有一个额外的非聚集唯一索引(parentId,+ 两个其他列)。
我在查询计划中看到的是,在删除 Grandchild 对象时,有两个昂贵的排序操作与删除混合在一起,我不明白它们为什么存在。
我应该注意什么来帮助这个删除操作更快?需要排序吗?如果我对 id 进行非规范化并将父 ID 添加到孙子表中会有所帮助吗?我是否愚蠢地建立了索引?
Pau*_*ite 17
要直接回答您的主要问题,排序是为了显示行以按索引键顺序更新运算符(在这种情况下执行删除)。这里的工作原理是对键进行排序将促进对索引的顺序访问。
这可能是一个很好的优化,但细节取决于您的硬件、受影响的页面在内存中的可能性以及排序是否可以在分配给它们的内存内完成。当优化器决定排序成本将通过与顺序索引访问相关的效率提高来偿还时,它会DMLRequestSort
在更新运算符上设置一个属性:
优化器还可以决定将更新拆分为单独的运算符,以维护聚集索引(或堆),然后是非聚集索引。通常,它会决定不止一次排序——首先对聚集索引键进行排序,然后再对非聚集索引进行排序。同样,在排序被认为是最佳的情况下,每个索引更新运算符都将DMLRequestSort
属性设置为 true。
综上所述,我首先要解决的问题是消除索引扫描,其中它们提供的连接运算符是嵌套循环连接,并删除急切的索引 spools,每次查询时都会将行插入空索引执行。急切的索引假脱机通常是您缺少有用的永久索引的最明显标志。索引假脱机操作符中的搜索谓词标识优化器想要索引的键。
缺少非聚集索引(需要急切索引假脱机)的表的示例是:
child6gc8Selections
gc9s
child7s
gc6s
Run Code Online (Sandbox Code Playgroud)
当前正在嵌套循环连接下扫描的表的示例是:
child1
parentObjectMessages
child8s
child7s
child6s
child5s
child4s
child3s
child2s
Run Code Online (Sandbox Code Playgroud)
以上面显示的示例为例,聚集索引扫描的输出列表为Id, parentObjectId
,嵌套循环连接谓词为child7s.parentObjectId = parentObject.Id
,连接输出列列表为child7s.Id
。
从这些信息中,似乎一个良好的访问方法(指数)child7s
的查询,这部分将被键入上parentObjectId
与Id
作为包含列。您应该能够找出如何最好地将其应用到您现有的索引策略中。
以下是优化器当前选择散列连接的表示例。我会检查这样的表以确保这是一个合理的访问方法:
child6gc8Selections
gc2s
gc5s
gc6Properties
Run Code Online (Sandbox Code Playgroud)
该表child2bigChild
还参与需要显式排序的合并连接。同样,我会检查是否可以避免这种情况。
一旦基本的索引问题得到解决,我们可以在必要时查看其他优化。