SQL Server:是否涵盖所有列的索引?


9

我们的团队继承了一个应用程序和关联的数据库。以前的开发人员似乎执行了一个规则,即每个表上的每个索引都有一个INCLUDE子句,以始终添加不属于键一部分的每个列。这些表平均具有2到5个索引或唯一约束以及外键。

目的是不管在数据库上引发什么查询,都是为了提高SELECT性能,因为访问是通过默认(但并非总是)检索所有列的ORM进行的。我们希望这样做的副作用是增加了存储需求(可能如此),并且增加了INSERT / UPDATE / DELETE的开销时间。

问题是,这是明智的策略吗?我们的团队具有SQL Server的历史,但是没有成员会认为自己是其内部行为的专家(尽管已经提出了一个问题,即如果该策略是最佳策略,那么现在是否将其作为默认策略?)。我们还应该期待其他哪些副作用(数据库服务器CPU /内存/ TempDB的使用情况等),或者上述某些假设是否正确?

此外,该应用程序既可以安装到SQL Server内部部署(自2012年以来的版本)中,也可以安装到Azure SQL中-如果我们为两者之间的任何差异或Azure的其他副作用做好了准备,方法?

Answers:


8

在此之前,我已经在特定索引上完成了此操作,以帮助经常运行的繁重查询。实际上,他们所做的就是创建了多个聚簇索引:当使用这些索引中的任何一个来查找行时,不需要额外的工作来查找真实聚簇索引(如果没有真实聚簇索引,则查找堆)中的其余数据。 。

这是明智的策略吗?

对于某些需要支持某些查询模式的索引,肯定可以。

但是要对所有索引执行此操作,我肯定会说不。

在实际不需要的地方做的工作将浪费很多空间,并且会大大降低插入/更新的速度。它可能会减慢尽可能多的读取查询的速度,因为每个索引页面保存的记录较少,因此任何需要引用索引块进行过滤但不使用所有其他列的查询都必须访问更多页面。这将使数据库更加耗费内存:这些页面将需要加载到缓冲池中,如果内存不足,则有可能弹出其他有用的页面。如果对那些索引使用压缩以减轻对存储和内存要求的影响,那么它将向CPU施加额外的负担。

因为访问是通过默认(但并非总是)通过ORM检索所有列的

这是在ORM(或仅是幼稚的ORM)的优化使用不佳的情况下出现的常见模式,在这种情况下,我已经看到SQL Server的索引顾问(和类似的第三方工具)建议包含许多INCLUDEd列的索引,因此我同意您的观点。建议这就是为什么以这种方式创建索引的原因。

但是,尽管这可能会使所有这些查询稍快一些,而其中一些查询却显着更快,但我怀疑在许多情况下,这样做的好处是如此之小,以至于不值得您使用通用工作集,磁盘空间和磁盘和内存之间的IO。

还请记住,ORM可能不会选择查询所涉及的所有表的所有列,因此,好处可能仅适用于当前请求的主要目标,并且当使用其他对象进行过滤时,较大的索引可能会对查询造成不利影响但不返回数据(SELECT * FROM table1 WHERE id IN (SELECT someID FROM table2 WHERE someColumn='DesiredValue')也许)。

使用多余空间的另一个考虑因素(尤其是在数据较大的情况下)将对您的备份策略产生影响:这些备份的存储和传输成本,潜在的还原时间等。

我们应该为两者之间的任何差异做好准备吗?

通常,我认为每种情况下这里的注意事项都是相同的,尽管在Azure中您可以调整服务层,从而可以更直接地看出由大索引施加的任何额外的内存/ IO成本,因此比起与之相比,基础架构成本更容易具有相对固定的一组硬件资源。如果使用标准/高级层而不是基于vcore的定价,那么标准中的IO成本将对您造成更大的影响,因为高级服务每DTU包含的IO明显更多。如果在Azure中使用多区域备份或冗余或其他非本地功能,则不必要的宽索引占用的额外空间可能会带来带宽成本。


我们继续进行了此删除操作。副作用是在某些表上,SELECT没有指定ORDER BY开始返回与以前相同的行,但是具有不同的任意顺序。
T2PS

这并不意外。根据定义,不带“ ORDER BY”的结果顺序是不确定的,并且可以在查询计划者决定采用其他方法时随时更改,这可能是由于索引更改或数据模式随着其增长而更改的结果。即使没有此更改,其他因素也可能在以后进行此类更改。如果您依赖于语句输出的顺序(即使是表面上的),则需要包括一个“ ORDER BY”来保证它。
David Spillett

哦,是的 先前的评论更多地是作为提醒笔记,供以后找到此答案的任何人使用。
T2PS

5

问题是,这是一个明智的策略吗?....(尽管已经提出了一个问题,即如果该策略是最佳策略,那么现在是否会成为默认策略?)

在大多数情况下,这不是明智的策略。原因是,在一般的OLTP数据库中,返回给最终用户的行不会太多。(概括)

您应该问自己的问题是,如果在关键列上进行搜索,那么该搜索操作将返回多少行?并对该列中的查询重复该操作。

考虑下表,返回了很多列, where SelectiveIDField= ...

select columnA,columnC, ... columnZ
FROM dbo.BigTable
Where SelectiveIDField= '225122141';

如果seek只能返回一行selectiveIDField,那么额外的键查找是一件坏事吗? (猜测您这里有聚集索引,否则查找RID)

它将只执行一次额外的键查找,一次额外的执行和join运算符。即使是10甚至100,影响会那么大吗?这也取决于查询的执行量以及执行时间的重要性。

在它可以忽略不计的情况下,只需创建索引SelectiveIDField并将其命名为一天,那么与写入损失相比,读取收益就不值得了。

简而言之,在我看来,在整个表上创建索引应该不是一种默认方法,除非您确实看到查询问题,并且可以通过添加整个覆盖索引来大幅度改善该问题。

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.