我最近在我维护的数据库中遇到了一个索引,其形式为:

CREATE INDEX [IX_Foo] ON [Foo]
( Id ASC )
INCLUDE 
( SubId )

在这种特殊情况下,我遇到的性能问题(对 Id 和 SubId 的 SELECT 过滤速度很慢)可以通过简单地将 SubId 列移动到适当的索引中而不是作为包含列来解决。

然而,这让我想到,我根本不理解包含列背后的推理,通常情况下,它们可能只是索引本身的一部分。即使我并不特别关心索引本身中的项目,在索引中包含列而不是简单地包含列也有任何缺点。

经过一些研究,我意识到索引列中的内容有很多限制(索引的最大宽度,以及某些无法索引的列类型,如“图像”)。在这些情况下,我可以看到您将被迫在索引页数据中包含该列。

我唯一能想到的是,如果 SubId 有更新,如果包含该列,则不需要重新定位该行(尽管需要更改索引中的值)。我还缺少其他东西吗?

我正在考虑检查数据库中的其他索引,并在可能的情况下适当移动索引中包含的列。这会是一个错误吗?

我主要对 MS SQL Server 感兴趣,但也欢迎有关其他数据库引擎的信息。

有帮助吗?

解决方案

到目前为止,答案都是正确的,但它们可能不足以传达您从覆盖索引中获得的信息。

就你而言,你有一张桌子 Foo 和一些字段,包括 Id (我假设是主键),以及 SubId 这是某种附加的 ID。

你还有一个索引 IX_Foo 我认为只有 Id 暂时在其中。

所以现在你需要找到 SubId 为了 Id=4.

SELECT Id, SubId
FROM Foo
WHERE Id=4
  • SQL Server 将查看 SELECT 语句并确定它可以使用 IX_Foo
  • 然后它会去搜索该值 Id=4 在你的索引中 IX_Foo
  • 当它找到它时,它现在需要的值 SubId, , 也
  • 非聚集索引 IX_Foo 将包含聚类键值
  • 使用该集群键值,SQL Server 将执行“书签查找”来定位整个数据行所在的实际数据页
  • 它将获取该页面并提取其值 SubId 从中
  • 它将返回这些值来满足您的查询

这里的要点是:一旦 SQL Server 找到您的 Id=4 在里面 IX_Foo 索引,然后需要执行另一个 I/O 操作,即书签查找,以获取整个数据行,以便能够找到 SubId 价值。

如果您有覆盖索引,例如 IX_Foo 还包括 SubId, ,消除了进行书签查找的额外 I/O。一旦值 Id=4 发现于 IX_Foo 索引,非聚集索引中的索引页还将包含以下值 SubId - SQL Server 现在可以返回您在 SELECT 查询中要求的两个值 没有 必须进行额外的(可能很昂贵,因此很慢)书签查找才能获取另一个 Id 列。

这是覆盖索引的主要好处 - 如果您只需要一两个额外的列,除了要查找的索引值之外,通过将这些值包含到索引本身中,您可以节省大量书签查找,从而显着加快速度。但是,您应该只包含很少的少量信息 - 不要将整个数据行复制到所有非聚集索引中!这不是重点。

更新: 权衡是这样的:如果您在 (Id, SubId) 上有索引,则索引中的所有页面都具有两列 - 整个索引树。

如果您 INCLUDE(SubId),则 SubId 字段仅出现在叶级别。

这意味着

  • SQL Server 无法搜索和比较 SubId(值不在索引树中)
  • 使用较少的空间,因为这些值仅位于叶级别

其他提示

之所以具有在索引的附加列是这样,当你做一个查询,只需要由索引使用的列则可以通过本身实现从索引查询。这样你节省一些时间和资源去回表。发生这种情况时,我们说指数是查询的覆盖的指数。

您可能不希望使“指标适当”的这一附加列部分原因是因为当你插入或更新该列,你就更有可能需要重新排序指标的部分。

使用包括在索引允许索引被用作覆盖索引(即某些查询可以单独使用该索引来满足,而不必执行书签查找到聚集索引),不添加这些列于实际该指数的树的一部分,从而保持了指数下行的大小。 (所包含的列仅添加到索引的叶节点)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top