【问题标题】:Non-clustered index and clustered index on the same column同一列上的非聚集索引和聚集索引
【发布时间】:2012-08-26 02:01:35
【问题描述】:

我在 Stackoverflow 中看到了 this 的帖子。第一个答案提到类似 聚集索引具有表的所有数据,而非聚集索引只有列 + 聚集索引的位置或行(如果它在堆上)(没有聚集的表index)。 非聚集索引如何获得聚集索引的位置?它只包含在 B 树中排序为节点的列值,每个节点都固定到该列具有该节点值的行,对吧?

【问题讨论】:

  • 如果在a 列上定义了非聚集索引,在b 上定义了聚集索引,那么将有两个单独的结构。聚集索引将包含根据b 中的值按排序顺序排列的行,然后非聚集索引会将a 中的值排列在带有指向数据的指针的 B 树中(根据值在b'). How can the non-clustered index accommodate another column b`?
  • 在这种情况下,您的非聚集索引将在其叶级页面中包含 b(聚集键)的值。如果在非聚集索引中找到 a 的值,但您的查询需要更多数据(例如 SELECT *......),则使用 b 值,键查找到聚集索引恰好找到实际的数据页并返回查询的值

标签: sql-server indexing b-tree clustered-index non-clustered-index


【解决方案1】:

假设您在谈论 SQL Server 并且还假设您的表一个聚集索引(您应该这样做)。

然后非聚集索引具有您在CREATE INDEX 语句中定义的列,加上它具有构成聚集索引(如果存在)的列。

该聚类键值是指向实际数据所在位置的“指针”。

如果查询执行器在你的非聚集索引中寻找一个值并找到一个匹配,那么

  • 任何一个你只关心那个价值 - 然后你就拿回那个价值

  • 非聚集索引也可能有一些包含 列(在叶级页面中),并且有了这些,查询可以满足(所有请求的列都存在),因此您可以取回您要求的值

  • 那么你想要的值并不全部在非聚集索引叶级页面中(如果你一直这样做SELECT * 尤其如此) 然后查询执行器必须从非聚集索引中获取聚集键值,然后返回聚集索引,执行所谓的键查找,在聚集索引中查找,并找到关联的存储完整行的数据页 -> 现在查询执行器可以返回您要求的值

对于一个很好的解释 - 见this blog post here。它说:

在非聚集索引中:
....
2.b。如果表具有聚集索引,或者索引位于索引视图上,则行定位器是该行的聚集索引键。 SQL 服务器通过使用以下方法搜索聚集索引来检索数据行 存储在 NonClustered 的 Leaf 行中的 Clustered index Key 索引。

或者参见this blog post in a whole series on SQL Server indexes,它也解释了存储在非聚集索引叶级页面中的“书签”。

【讨论】:

  • 我不明白,非聚集索引本身持有一个指向整行数据的指针,为什么又需要聚集索引呢?
  • @Cupidvogel:非聚集索引的叶级页面有自己的索引列、任何包含的列,以及聚集键的值 - 但那是 NOT 实际的数据页位置。找到 NC 叶级页面并且需要完整的数据页面后,需要进行 键查找(集群索引中的索引查找)才能到达实际的数据页面
  • 那么在只有非聚集索引没有聚集索引的情况下如何查找呢? 包含的列是什么意思?
  • @Cupidvogel:如果您没有聚集索引 - 那么,在堆表的情况下,您有一个行标识符 在非聚集索引的叶级页面中 - 将 指针 指向数据页面 - 每次数据页面可能移动时也需要更新该页面 - 这很痛苦并且对性能有很大影响。堆是一团糟——尽可能避免它们!
  • @Cupidvogel:是的,但是页面拆分不会改变集群键值!
【解决方案2】:

很容易想象成这样:

您有一个客户表,例如 customer(id, name, age, adress)。在这张表上,您有一个关于年龄的聚集索引。这意味着您的数据在硬盘上按年龄排序。当您想要进行范围查询时,这非常有用:

SELECT * FROM customer WHERE age > 18;

然后只需进行几次顺序读取,即可从您的硬盘驱动器中获取数据。如果索引是非集群的,您将不得不为每个匹配的客户元组进行一次磁盘访问(包括数据的查找)。

也许对于您的应用程序,您还需要通过 id 访问用户。这意味着如果没有额外的 id 索引,您将不得不遍历整个文件才能找到特定的 id,因为它是按年龄排序的,而且您没有索引!为避免这种情况,您需要在 id 上创建一个第二个索引。现在您可以在此索引中搜索一个 id,并且包含您正在寻找的客户的索引的叶子指向您在磁盘上(按年龄聚集的)数据中找到元组的位置。这样你就不能读取整个表需要更少的磁盘访问(通常 2 用于索引查找 + 1 用于获取元组)。

编辑: 我没有看到你在谈论同一专栏。我可以想象的一件事是,出于上述原因,您在一列上执行了一个聚集索引,例如,另一个组合索引 和另一列。这对于进行仅索引查找 很有用,此时您在索引中拥有所有必需的属性,并且根本不需要进行页面提取。另一个原因是对于范围查询有一个聚集的 B+-Index,对于相等查询有一个 Hash-Index。但我认为这里的好处可以忽略不计。

希望这有帮助!

【讨论】:

    猜你喜欢
    • 2013-08-07
    • 1970-01-01
    • 2021-01-14
    • 2019-02-13
    • 2016-01-05
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多