理解SSMS中的索引和缺失索引建议

时间:2015-07-24 12:45:58

标签: sql sql-server sql-server-2012

理解指数&缺少索引建议

我试图更好地理解索引。我有很多阅读要做的事情,并从其他SO帖子中找到了一些宝贵的资源,其中一些我已经阅读过了,其他一些我还需要阅读。与此同时,我试图从我的数据库中获得更好的性能。

我已经了解到覆盖索引的性能会比单个列上的索引更好,因此我决定首先删除单个索引,然后让建议的查询执行计划推荐索引。

SSMS INDEX建议

CREATE NONCLUSTERED INDEX IX_my_index_name
ON [dbo].[my_table] ([field_a],[field_b])
INCLUDE (
   [field_1]
  ,[field_2]
  ,[field_3]
  ,[field_4]
  ,[field_5]
  ,[field_6]
)

表详情

字段1-6是我常用来加入我使用的2个表的列。田野a& b在我运行的几个耗时的查询的where子句中找到。

我理解使用字段1-6,因为大多数情况下它们都包含许多不同的值,但field a只有大约75个不同的值,field b只有3个不同的值。这是一张包含70MM记录的表格。

请注意,这是一个堆。此表上的所有记录都来自另一个具有主键的表,因此它带有唯一值,但它未在此表上设置为键或唯一索引。 SSMS并不建议在该索引中包含该列。想知道我应该如何处理这张表中的独特价值?一个集群的,独特的索引,我猜?

我的问题

  1. 我想了解这个索引建议背后的逻辑。给出关于列a和amp;中的类似值的信息。 b,为什么推荐这个?

  2. 我想了解ON列和INCLUDE列之间的区别?

4 个答案:

答案 0 :(得分:3)

索引中的ON列可用于搜索行。这些字段包含在索引树中。找到行后,如果需要任何其他列,例如select part或join中的字段,则必须从表中获取它们。这在执行计划中称为key lookup

如果索引有多个列,并且并非在where子句中指定了所有列,则只要给出了字段,就可以从第一个开始使用这些列。例如,索引有字段A,B,C,D,where子句有字段A,B和D,那么只有A和B可用于获取数据。

如果表具有聚簇索引,则聚簇索引中的键值将存储在其他索引中,并用于从表本身中查找行。如果没有聚簇索引,则以类似的方式使用RID(行ID)来定位表中的行。

索引中的include列是附加列,它们的数据存储在非聚集索引的叶级别。这样SQL Server就可以直接从那里读取数据并跳过读取表的整个部分。这称为covering index

答案 1 :(得分:2)

包含索引(或覆盖索引)允许SQL Server查找需要满足索引本身查询的所有信息,而无需返回实际数据页以获取所请求的信息。它是数据的副本,但所包含部分中的列不用于搜索 - 只是为了返回数据。您的表应始终(除非在非常非常特殊的条件下,例如当您执行大量插入时)在其上具有聚簇索引。

使用索引的原因是减少SQL Server必须读取整个表以返回数据的扫描次数。通过使用索引,SQL Server可以只查找和读取返回所请求行的页面。如果列的值有限,SQL Server可能会决定忽略该索引并执行扫描。您必须查看生成的查询计划,以查看SQL Server是否使用索引。如果SQL Server建议索引,通常意味着SQL Server引擎将使用索引。但是每个索引都有成本 - 需要维护它,所以不要创建太多的索引。

答案 2 :(得分:2)

我想了解这个索引建议背后的逻辑。给出关于字段a&中的类似值的信息。 b,为什么推荐这个?

tl; dr 这完全取决于您查询数据的方式。

这是一个难以回答的问题,因为它取决于您查询表的频率,查询类型,服务器负载以及其他一些事情。

例如,如果您在许多查询中使用字段1-6,这些查询使用'简单计划'然后SQL Server不会认为它们是好的canadites用于索引。一个简单的计划'或者是一个简单的计划'是SQL SERVER为任何它认为不够复杂的查询提供的计划,以生成完整的计划。

长时间运行的查询将被充分探索'通过SQL SERVER。将计算并存储组织图。这会提醒SQL SERVER现有索引是不够的。 A'充分探索'计划是SQL SERVER已扩展并为其生成非简单查询计划的计划。

我想了解ON列和INCLUDE列之间的区别吗?

On和Include之间的区别是。

开启语句将在索引中包含该列。这意味着列在存储时是索引的一部分。

所以索引就像这样工作。表的索引形成B树。 B树中的节点包含聚簇索引值,以及其余值的ROWID。如果您在B-Tree中搜索不是聚类索引的一部分的值,它将首先找到聚类索引,然后它将拥有其余数据的内存地址。然后,它将对该内存地址执行第二次查找以查找其他值。

INCLUDE 子句添加最低/叶级别的数据,而不是索引树中的数据。这使得索引更小,因为它不是树的一部分

答案 3 :(得分:2)

我要问的第一件事是,是否有充分的理由让这个大小的表没有聚集索引?群集密钥甚至不必是唯一的(如果没有,SQL Server将添加' uniquifier'尽管它通常最好使用IDENTITY列。)

回答你的两个问题:

1)索引建议与您正在运行的查询相关。根据经验,建议的列将匹配查询优化器用于探测表的列,因此如果您有类似的查询:

SELECT field1, field2, field3
FROM   table1
WHERE  field4 = 1 AND field5 = 'bob'

建议的索引可能位于field4field5列,并且按选择性顺序排列(即首先是值变化最大的列)。它可能包含其他列(例如field1, field2, field3),因为查询优化器只需访问索引即可获取该数据,而不是访问数据页。

另请注意,有时建议的索引并不总是您自己选择的索引。如果连接多个表,查询优化器将根据可用的索引和统计信息选择它认为最适合数据的执行计划。当最好的计划可能反过来时,它可能会循环遍历一个表并探测到另一个表。您必须检查实际的查询执行计划以查看正在进行的操作。

如果您知道您的查询具有足够的选择性以深入查看小范围的记录(例如,具有类似WHERE table1.field1 = 1 AND table1.field2 = 'abc' AND table1.field3 = '2015-07-01' ...的where子句),则可以添加涵盖所有引用列的索引。这可能会影响查询优化器扫描此索引以获取少量行以加入另一个表,而不是执行扫描。

根据经验,在检查执行计划时,一个好的起点是尝试消除扫描,服务器将读取大范围的行,并提供缩小必须的数据量的索引。被处理。

2)我认为其他人可能已经很好地解释了这一点 - 所包含的列是存在的,因此当读取索引时,服务器不必读取数据页面来获取这些值;它们也存储在索引中。

很多人在阅读有关索引的内容时可能会遇到的初步反应&#39;是的,为什么我不添加一大堆执行此操作的索引&#34;或者&#34;为什么我不添加涵盖所有列的索引&#34;。< / p>

在某些情况下(通常是具有窄列的小表,例如多对多连接表),这很有用。但是,您添加的每个索引都会产生一些成本:

首先,每次更新或向表中插入值时,都必须更新索引。这意味着您将不得不应对锁定,锁定升级问题(可能是死锁),页面拆分以及相关的碎片。有多种方法可以缓解这些问题,例如使用适当的填充因子来允许将更多值插入索引页而不必将其拆分。

其次,索引占用空间。至少,索引将包含您使用的键值以及RID(在堆中)或群集键(在具有聚簇索引的表中)。覆盖索引还包含所包含列的副本。如果这些是大型列(例如大变量),那么索引可能非常大,并且对于表索引加起来大于表本身并不是闻所未闻。请注意,在列和总大小方面,索引的大小也有限制。由于聚簇键始终包含在具有聚簇索引的表上的非聚簇索引中(聚簇索引位于数据页本身上),这意味着较小的聚簇键更好。虽然您可以使用复合索引,但这可能是几个字节宽,虽然您可以使用非唯一键,但SQL Server会将该uniquifier添加到它,这是另外4个字节。最佳做法是使用标识列(int或bigint,如果您设想表中的行数超过20亿)。身份也总是递增,因此在插入新记录时,您不会在数据页中获得页面拆分,因为它总是会在表的末尾。

所以tl; dr;是:

建议的索引可能很有用,但通常不能提供最佳索引。如果您知道数据的结构以及如何查询数据,则可以构建包含常用探测键的索引。

始终按照 selective (即具有最多值的列)的顺序对索引中的列进行排序。这可能看似违反直觉,但它允许SQL Server更快地找到您想要的数据,而且读取次数更少。

包含的列很有用,但通常只在小列(例如整数)时才有用。如果您的查询需要表中的六列,并且索引仅涵盖其中五列,则SQL Server仍然必须访问数据页面,因此在这种情况下,如果没有包含的列,您最好关闭,因为它们只占用空间并且有维护费用。