【问题标题】:Why NonClustered index scan faster than Clustered Index scan?为什么非聚集索引扫描比聚集索引扫描快?
【发布时间】:2015-01-27 10:06:21
【问题描述】:

据我所知,堆表是没有聚集索引并且没有物理顺序的表。 我有一个 120k 行的堆表“扫描”,我正在使用这个选择:

SELECT id FROM scan

如果我为“id”列创建非聚集索引,我会得到 223 次物理读取。 如果我删除非聚集索引并更改表以使“id”成为我的主键(以及我的聚集索引),我将获得 515 次物理读取

如果聚集索引表是这样的图片:

为什么聚集索引扫描像表扫描一样工作? (或者在检索所有行的情况下更糟)。为什么它不使用块较少且已经具有我需要的ID的“聚集索引表”?

【问题讨论】:

    标签: sql sql-server indexing sql-execution-plan full-table-scan


    【解决方案1】:

    SQL Server 索引是 b 树。非聚集索引只包含索引列,b 树的叶节点是指向适当数据页的指针。聚簇索引则不同:它的叶节点是数据页本身,聚簇索引的 b-tree 成为表本身的后备存储;该表的堆不再存在。

    您的非聚集索引包含一个可能是整数列。这是一个小而紧凑的索引。您的查询select id from scan 有一个覆盖索引:只需检查索引即可满足查询,这就是正在发生的事情。但是,如果您的查询包括不在索引中的列,假设优化器选择使用非聚集索引,则需要进行额外的查找以从聚集索引或堆中获取所需的数据页。

    要了解发生了什么,您需要检查优化器选择的执行计划:

    【讨论】:

    • 知道了。我的错误是认为非集群和集群的 b 树是等价的。
    • @Mucida:两种索引类型的b-tree(导航树)大致相同 - 显着不同的是叶级别树中的节点...
    • @marc_s,但是当我在表中同时有一个集群索引和一个非集群索引时,非聚集 b 树的叶子会改变并包括聚集索引?当我选择这两个索引列时,它们似乎运行得很快,即使是使用非聚集索引扫描的执行计划
    • @Mucida:是的,这就是我试图解释的:两种索引类型的 导航树 大致相同 - 带有索引列的 b 树。 叶级非常不同:聚集索引的实际数据页,非聚集索引包含索引列,加上聚集索引列(如果存在聚集索引- 如果聚集索引不存在,则为 RID = 行标识符)
    【解决方案2】:

    聚集索引通常与堆中的相同数据一样大(假设相同的页面充满度)。由于额外的 B 树级别,它应该比堆使用的读取多一点。

    CI 不能小于堆。我不明白你为什么会这么想。分区的大部分大小(无论是堆还是树)都在数据中。

    请注意,更少的物理读取并不一定意味着查询更快。随机 IO 可能比顺序 IO 慢 100 倍。

    【讨论】:

    • 问题在于聚集索引扫描使用的物理读取数量与表扫描相同。为什么在这种情况下非聚集索引扫描比聚集索引扫描运行得这么快?
    • 我在第一段中解释了这一点。你到底有什么不明白的?为什么你认为 CI 扫描应该更快?
    • 我认为这是我的疑问:图片是否真的代表聚集索引(带有指向其余行的指针)?或者只是非聚集索引是这样的?如果最后一个是正确的,我可以理解为什么 NCI 比 CI 工作得更好
    • CI 包含所有内联列。 NCI 仅包含您指定的那些列以及所有 CI 键列。
    【解决方案3】:

    何时使用聚集索引-

    查询注意事项:
    1) 使用 BETWEEN、>、>=、 3)使用JOIN子句;通常这些是外键列
    4) 使用 ORDER BY 或 GROUP BY 子句。在 ORDER BY 或 GROUP BY 子句中指定的列上的索引可能不需要数据库引擎对数据进行排序,因为这些行已经排序。这提高了查询性能。

    列注意事项: 考虑具有以下一个或多个属性的列: 1) 是唯一的或包含许多不同的值 2) 定义为 IDENTITY 因为该列在表中保证是唯一的 3) 经常用于对从表中检索到的数据进行排序

    聚集索引不是以下属性的好选择: 1) 变化频繁的列 2) 宽键

    何时使用非聚集索引-

    查询注意事项:
    1) 使用 JOIN 或 GROUP BY 子句。在涉及连接和分组操作的列上创建多个非聚集索引,并在任何外键列上创建一个聚集索引。
    2) 不返回大结果集的查询
    3) 包含查询的搜索条件中经常涉及的列,例如返回完全匹配的 WHERE 子句

    列注意事项
    考虑具有以下一个或多个属性的列:
    1)覆盖查询。有关详细信息,请参阅包含列的索引
    2) 很多不同的值,例如姓和名的组合,如果聚集索引用于其他列
    3) 经常用于对从表中检索到的数据进行排序

    数据库注意事项:
    1) 更新要求不高,但数据量大的数据库或表,可以从很多非聚集索引中受益,提高查询性能。
    2) 包含大量更新表的在线事务处理应用程序和数据库应避免过度索引。此外,索引应该很窄,即尽可能少的列。

    【讨论】:

      【解决方案4】:

      尝试运行

      DBCC DROPCLEANBUFFERS
      

      在查询之前...

      如果你真的想比较它们。 优化查询时,物理读取与逻辑读取的含义不同

      【讨论】:

      • 鉴于他的表似乎只有 4MB 大小,我想我们可以假设他这样做了。如果不是这样,他就不会收到一次物理读取。
      • 好点usr,但我不确定在alter table命令之后是否维护表的缓存数据......
      • 图片真的代表一个聚集索引(带有指向其余行的指针)吗?或者只是非聚集索引是这样的?如果最后一个是正确的,我可以理解为什么 NCI 比 CI 工作得更好
      猜你喜欢
      • 2014-12-18
      • 1970-01-01
      • 2019-08-26
      • 1970-01-01
      • 2010-09-06
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      • 1970-01-01
      相关资源
      最近更新 更多