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

Jon*_*Jon 7 sql sql-server sql-server-2012

了解指数和缺失指数建议

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

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

SSMS索引建议

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]
)
Run Code Online (Sandbox Code Playgroud)

表详细信息

字段1-6是我通常用来连接我正在使用的2个表的列.字段a和b可以在我运行的几个耗时的查询的where子句中找到.

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

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

我的问题

  1. 我想了解这个索引建议背后的逻辑.鉴于有关a和b列中类似值的信息,为什么建议这样做?

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

Ed *_*d B 2

我要问的第一件事是,这样大小的表没有聚集索引是否有充分的理由?聚集键甚至不必是唯一的(如果不是,SQL Server 将向其添加“唯一符”,尽管通常最好使用 IDENTITY 列)。

回答你的两个问题:

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

SELECT field1, field2, field3
FROM   table1
WHERE  field4 = 1 AND field5 = 'bob'
Run Code Online (Sandbox Code Playgroud)

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

另请注意,有时建议的索引并不总是您自己选择的索引。如果连接多个表,查询优化器将根据可用索引和统计信息选择它认为最适合数据的执行计划。它可能会遍历一张表并探索另一张表,而最佳可能的计划可能会以相反的方式进行。您必须检查实际的查询执行计划以了解发生了什么。

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

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

2)我认为其他人现在可能已经解释得足够好了 - 包含的列在那里,这样当读取索引时,服务器就不必读取数据页来获取这些值;它们也存储在索引中。

许多人在读到此类“覆盖索引”时的最初反应可能是“为什么我不添加一大堆执行此操作的索引”,或者“为什么我不添加一个覆盖所有索引的索引”?列”。

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

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

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

所以 tl;dr; 是:

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

始终按选择性顺序对索引中的列进行排序(即,值最多的列在前)。这可能看起来违反直觉,但它允许 SQL Server 以更少的读取速度更快地找到您想要的数据。

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