查看聚集索引查找超过 50 万行需要 7 分钟

Posted

技术标签:

【中文标题】查看聚集索引查找超过 50 万行需要 7 分钟【英文标题】:View Clustered Index Seek over 0.5 million rows takes 7 minutes 【发布时间】:2013-09-07 00:08:09 【问题描述】:

看看这个执行计划:http://sdrv.ms/1agLg7K 不是估计的,是实际的。来自大约 30 分钟的实际执行。

选择第二条语句(占用总执行时间的 47.8% - 大约 15 分钟)。 查看该语句中的顶部操作 – View Clustered Index Seek over _Security_Tuple4。 该操作花费了语句的 51.2%——大约 7 分钟。

视图包含大约 0.5M 行(作为参考,log2(0.5M) ~= 19 – 考虑到索引树节点大小为 2,仅 19 步,实际上可能更高)。 该运算符的结果是零行(与估计不匹配,但暂时不要介意)。 实际执行 - 零。

所以问题是:哔哔声怎么可能需要 7 分钟?! (当然,我该如何解决?)


编辑澄清一下我在这里的问题。 我对与性能相关的一般建议感兴趣,例如“查看索引”、“查看大小”、“参数嗅探”、“针对不同数据的不同执行计划”等。 我已经知道这一切,我可以自己进行所有这些分析。

我真正需要的是知道什么会导致某个特定的聚集索引搜索如此缓慢,然后我可以做些什么来加快它的速度

不是整个查询。不是查询的任何部分。 只是一个特定的索引搜索。END EDIT


还要注意第二个和第三个最昂贵的操作是如何分别查找 _Security_Tuple3 和 _Security_Tuple2 的,它们只占用 7.5% 和 3.7% 的时间。同时,_Security_Tuple3 包含大约 280 万行,是 _Security_Tuple4 的 6 倍。

还有一些背景:

    这是该项目中唯一一个行为不端的数据库。 有几十个具有相同架构的其他数据库,它们都没有出现这个问题。 第一次发现这个问题,发现索引99%都是碎片化的。 重建索引确实加快了速度,但并不显着:整个查询在重建前用了 45 分钟,在重建后用了 30 分钟。 在使用数据库时,我注意到像“select count(*) from _Security_Tuple4”这样的简单查询需要几分钟时间。什么鬼?! 但是,它们在第一次运行时只用了几分钟,然后就立即运行了。 问题是没有连接到特定的服务器,也没有连接到特定的 SQL Server 实例:如果我备份数据库然后在另一台计算机上恢复它,行为保持不变。李>

【问题讨论】:

【参考方案1】:

首先我想在这里指出一个小误解:虽然据说删除语句占用了整个执行时间的近 48%,但这并不一定意味着它占用了所需时间的 48%;事实上,在查询计划的那一部分内分配的 51% 绝对不应该被解释为占用整个操作的“一半时间”!

无论如何,按照您的说法,“第一次”执行表的 COUNT(*) 需要几分钟,我倾向于说您有与所述表/视图相关的 IO 问题。就我个人而言,我不太喜欢物化视图,因此我对它们以及它们在内部的行为方式没有真正的经验,但通常我会建议碎片化正在对底层存储系统造成损害。它第二次运行得很快的原因是因为从缓存中访问页面比从磁盘获取页面时要快得多,尤其是当它们到处都是时。 (视图中是否有任何(最大)字段?)

无论如何,要找出需要这么长时间的原因,我建议您宁愿将此代码从当前所在的触发器中取出,“伪造”一个插入和删除的表,然后尝试再次运行查询,添加时间戳和/ 或使用诸如 SQL Sentry Plan Explorer 之类的程序来查看每个部分真正需要多长时间(当您从程序中运行脚本时,它有一个持续时间列)。 很可能是您看错了部分;经验表明,成本和实际执行时间并不总是像我们想的那样相关。

【讨论】:

非常感谢您的措辞和深思熟虑的回答。能否请您指出一些内容以了解“成本”的真正含义以及它与持续时间的关系? 遗憾的是,没有将成本与处理时间(又名挂钟时间)联系起来的神奇公式。在 MSSQL7 中,我的(天真?)印象是它们至少是“直接相关的”,但随着版本的发展,我已经艰难地了解到它们是不同的野兽。仅供参考:***.com/questions/564717/… 提供了一些额外的解释。这里要记住的是,成本是针对服务器上的所有资源以及当许多人/应用程序访问它时如何分散它们的;与大多数时间测试不同。 好的,我知道了。非常感谢您的解释。尽管你没有解决我的问题,但你教会了我一些我以前不知道的东西,并给了我一些关于下一步该往哪里看的想法,这超出了我的期望。所以这是你来之不易的赏金。 :-) 谢谢,祝你好运,找出触发器发生了什么。【参考方案2】:

观察包括:

    这是您正在使用的这些数据库中最大的一个吗?如果是这样,大小对优化器很重要。它将为大型数据集与较小的数据集制定完全不同的计划。 估计的行数和实际行数相差很大。这在第四个查询中最为明显。 "delete c from @alternativeRoutes...." 其中 _Security_Tuple5 估计返回 16 行,但实际上使用了 235,904 行。对于这么多行,索引扫描可能比索引搜索更高效。表格上的统计数据是最新的还是需要更新? “从_Security_Tuple4 中选择计数(*)”需要几分钟,这是第一次。第二次是即时的。这是因为数据现在都缓存在内存中(直到过期),并且第二次查询速度很快。 因为问题随着数据库移动,所以统计信息、任何缺失的索引等都在数据库中。我还建议检查索引是否与使用相同架构的其他数据库匹配。

这不是一个完整的分析,但它给你一些东西来看看。

【讨论】:

你没有回答我问的问题。我做了所有这些分析,我已经采取了一些措施来减轻你所说的一些可能的问题。我要问的问题很简单:什么会导致一个简单的聚集索引查找超过 50 万条记录运行 7 分钟? 再一次:我不关心优化器采用什么计划,或者数据是否在缓存中,或者缺少哪些索引等等。我可以自己完成所有这些分析好吧。我需要知道的是,为什么索引搜索这么慢以及我可以做些什么来加快它的速度。不是整个查询。只是一个特定的索引搜索。 @FyodorSoikin - 我不在你的服务器上,我只有你的查询计划要看。那么,我怎么知道呢?你的数据库损坏了吗?严重阻塞了那个数据库? (但从什么开始?)数据库是否存在于有问题的不同驱动器上?在 USB 闪存驱动器上?为什么你认为它在第一次选择后会“立即”运行?就说我不知道​​,但我希望其他人可以提供帮助。 @FyodorSoikin - 您的问题有近 2500 个字符,但您真正要问的是如何提高聚集索引查找性能?如果您不想要有关调整查询的建议,请不要发布执行计划并将所有额外的噪音排除在外。而是将执行计划发布到有问题的聚集索引搜索。 @brian,你能详细说明一下吗?如何获得搜索计划?【参考方案3】:

费多尔,

第一:

问题既没有连接到特定的服务器,也没有连接到特定的 SQL Server 实例:如果我备份数据库然后在另一台计算机上恢复它,行为保持不变。

我假设您:a) 在隔离环境中运行此查询,b) 数据未发生突变。

这对吗?

第二:在此处发布您的 CREATE INDEX 脚本。你有一个有趣的填充因子吗? SORT_IN_TEMPDB?

第三:你的ParentId、ObjectId是什么类型? int、smallint、uniqueidentifier、varchar?

【讨论】:

首先:我在本地计算机上运行此查询,没有运行其他任何东西。但是,数据可能“处于突变状态”,具体取决于您的意思。此查询计划正在更新这些相同的索引:您在图中向左看,您会看到“更新索引”节点。 第二:在_Security_Tuple4(Id1,Id2,Id3,Id4,SubjectId,PrivilegeId,DistancesJoined)上创建唯一聚集索引_Security_Tuple4_PK

以上是关于查看聚集索引查找超过 50 万行需要 7 分钟的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 2017 上创建具有 800+ 百万行的现有分区表的列存储索引

聚集索引和非聚集索引

SQL Server 聚集索引 clustered index 非聚集索引Nonclustered Indexes键查找查找Key Lookup执行计划过程详解

如何将非聚集索引变成覆盖索引

07.索引-非聚集索引-Key Lookup &RID Lookup

Azure SQL、聚集列存储索引、“TOP”性能