23

我从来没有真正理解这两个索引之间的区别,有人可以解释一下区别是什么(性能方面,索引结构在数据库中的样子,存储方面等)?

包含索引

CREATE NONCLUSTERED INDEX IX_Address_PostalCode  
ON Person.Address (PostalCode) 
INCLUDE (AddressLine1, AddressLine2, City, StateProvinceID); 

“正常”指数

CREATE NONCLUSTERED INDEX IX_Address_PostalCode  
ON Person.Address (PostalCode, AddressLine1, AddressLine2, City, StateProvinceID);
4

3 回答 3

21

索引的内部存储使用 B-Tree 结构,由“索引页”(根页和所有中间页)和“索引数据页”(仅叶页)组成。

注意不要将“索引数据页”与存储大部分实际数据列的“数据页”(聚集索引的叶页)混淆。

  • 只有索引列存储在索引页上。
  • 通过在部分中放置一些列,INCLUDE每个索引键存储的数据更少。
  • 这意味着需要更少的页面来保存索引键。(更容易将这些经常使用的页面在内存中缓存更长时间。
  • 并且树中的级别可能更少。(在这种情况下,性能优势可能会大得多,因为每个树级别的遍历都是另一个磁盘访问。

当使用索引时,索引键用于通过索引页面导航到正确的索引数据页面。

  • 如果索引有INCLUDE列,则该数据在查询需要时立即可用。
  • 如果查询需要索引键或列中不可用的INCLUDE列,则需要对聚集索引中的正确行进行额外的“书签查找”(如果未定义聚集索引,则需要堆)。

一些需要注意的事情,希望能解决你的一些困惑:

  • 如果您的查询中的索引和过滤器的键没有足够的选择性,那么索引将被忽略(无论您的INCLUDE列中有什么)。
  • 您创建的每个索引都有 INSERT 和 UPDATE 语句的开销;“更大”的索引更是如此。(更大也适用于INCLUDE列。)
  • 因此,虽然您理论上可以创建大量包含列的大索引来匹配访问路径的所有排列:但它会适得其反。

值得注意的是,在INCLUDE将列作为功能添加之前:

  • 扩展索引的键以包含索引/过滤器中不需要的列是一种常见的索引调整“技巧”。(称为覆盖指数。)
  • 这些列通常需要在输出列中或作为连接其他表的参考列。
  • 这将避免臭名昭著的“书签查找”,但缺点是使索引“更宽”而不是严格必要的。
  • 事实上,索引中较早的列通常已经标识了一个唯一行,这意味着如果不是为了“避免书签查找”的好处,额外包含的列将是完全多余的。
  • INCLUDE列基本上可以更有效地获得相同的好处。

注意需要指出的非常重要的一点。INCLUDE如果您习惯于总是将查询编写为SELECT * .... 通过返回所有列,您基本上可以确保在任何情况下都需要进行书签查找。

于 2017-01-22T14:12:31.583 回答
6

在第一个索引中,in Index pageonlyPostalCode是键列,并且AddressLine1, AddressLine2, City, StateProvinceID是叶节点的一部分,以避免key/RID查找

当我的表将始终被过滤PostalCode并且这些列中的任何一个都将成为过滤的AddressLine1, AddressLine2, City, StateProvinceID一部分select而不是过滤时,我会更喜欢第一个索引

select AddressLine1, AddressLine2, City, StateProvinceID
from Person.Address 
Where PostalCode=  

在第二个索引中,Index page将有五个关键列PostalCode, AddressLine1, AddressLine2, City, StateProvinceID

当我有可能过滤数据时,我会更喜欢第二个索引

Where PostalCode = And AddressLine1 = 

或者

Where PostalCode = And AddressLine2 = 

或者

Where PostalCode = And AddressLine1  = and AddressLine2 = 

等等..

在任何情况下,索引中的第一列都应该是过滤的一部分以利用索引

于 2017-01-22T13:29:49.823 回答
2

在第一个示例中,只有索引列:PostalCode 存储在索引树中,所有其他列存储在索引的叶级。这使索引的大小更小,如果您不使用 where、Join、group by 对其他列但仅对 PostalCode 使用,这将非常有用。

在第二个索引中,所有列的所有数据都存储在索引树中,这会使索引更大,但如果您要使用 WHERE/JOIN/GROUP BY/ORDER By 语句中的任何列,这将很有用。

包含列可以在选择列表中指定数据时更快地检索数据。

例如,如果您正在运行:

SELECT PostalCode, AddressLine1, AddressLine2, City, StateProvinceID 
FROM Person.Address 
Where PostalCode= 'A1234'

这将受益于在 PostalCode 上创建索引并包括所有其他列

另一方面,如果您正在运行:

SELECT PostalCode, AddressLine1, AddressLine2, City, StateProvinceID 
FROM Person.Address 
Where PostalCode= 'A1234' or City = 'London' or StateProvinceID = 1 or AddressLine1 = 'street A' or AddressLine2 = 'StreetB'

这将受益于索引中的所有列

看看下面的链接,这些可能对您的查询有更多帮助

包含列的索引:https ://msdn.microsoft.com/en-us/library/ms190806(v=sql.105).aspx

表和索引组织:https ://msdn.microsoft.com/en-us/library/ms189051(v=sql.105).aspx

于 2017-01-22T13:45:06.273 回答