15

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

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

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

然而,这让我想到,我根本不理解包含列背后的原因,一般来说,它们可能只是索引本身的一部分。即使我并不特别关心索引本身中的项目,在索引中包含列而不是简单地包含在内有任何不利之处。

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

我唯一能想到的是,如果 SubId 有更新,如果包含列,则不需要重新定位行(尽管需要更改索引中的值)。还有什么我想念的吗?

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

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

4

3 回答 3

9

到目前为止的答案都是正确的 - 但它们可能无法充分传达您从覆盖索引中获得的信息。

在您的情况下,您有一个表Foo和一些字段,包括一个Id(我假设它是主键),以及一个SubId是某种附加 ID。

您还有一个索引IX_Foo,我认为它现在只包含Id在其中。

所以现在你需要找到SubIdfor 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 ServerId=4IX_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 上搜索和比较(值不在索引树中)
  • 由于值仅在叶级别上,因此使用的空间更少
于 2010-03-13T09:03:59.320 回答
7

在索引中有一个附加列的原因是,当您执行只需要索引使用的列的查询时,您可以自己完成索引中的查询。通过这种方式,您可以节省一些时间和资源。当这种情况发生时,我们说索引是查询的覆盖索引。

您可能不想将此附加列作为“正确索引”的一部分的原因是,当您对该列进行插入或更新时,您更有可能需要对索引的某些部分进行重新排序。

于 2010-03-13T01:43:53.223 回答
3

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

于 2010-03-13T01:44:36.293 回答