包含列的索引,有啥区别?
Posted
技术标签:
【中文标题】包含列的索引,有啥区别?【英文标题】:Indexes with included columns, what's the difference?包含列的索引,有什么区别? 【发布时间】:2017-06-07 01:56:54 【问题描述】:我从来没有真正理解这两个索引之间的区别,谁能解释一下区别是什么(性能方面,索引结构在数据库中的样子,存储方面等)?
收录索引
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);
【问题讨论】:
文档中有哪些不明白的地方? msdn.microsoft.com/en-us/library/ms190806.aspx. 鉴于文档,我基本上可以为我的所有查询创建包含索引,从而减少解释器进行任何表扫描的需要。我想这有一些缺点,我正在寻找一些澄清。 。 .这些被称为相关查询的覆盖索引。它们可以极大地提高 select 查询的性能。它们确实需要更多空间并减慢数据修改速度。 我想我想要一些关于何时使用包含列以及何时避免使用它们的最佳实践指南 + 一些关于存储方面和性能的一般信息 在存在更多索引的情况下,更新变得更加昂贵,因为当列值更改时,SQL 必须更新它的所有副本。引用列的索引越多,要更新的副本就越多。 【参考方案1】:在第一个索引中,Index page
中只有 PostalCode
是键列,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 =
等等..
在任何情况下,索引中的第一列都应该是过滤的一部分以利用索引
【讨论】:
select *
并不是一个很好的例子,因为只要select
返回任何不在索引键中或包含的列:就需要进行书签查找。【参考方案2】:
在第一个示例中,只有索引列:PostalCode 存储在索引树中,所有其他列存储在索引的叶级。这使得索引的大小更小,如果您不使用 where、Join、group by 对其他列但仅对 PostalCode 使用,这将非常有用。
在第二个索引中,所有列的所有数据都存储在索引树中,这会使索引更大,但如果您要使用 WHERE/JOIN/GROUP BY/ORDER 中的任何列,这将很有用通过语句。
在选择列表中指定列时,包含列可以更快地检索数据。
例如,如果您正在跑步:
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
【讨论】:
在第 1 段中:如果PostalCode
足够选择性(即缩小到少量行),它仍然可以比扫描表更有效,所以index 在您提到的其他情况下仍然有用。 JOIN
有另一个考虑:如果用作查找其他表的参考,那么 INCLUDE
很棒!如果用于从其他表中过滤,那么现有的键列选择性就会发挥作用,并且可能有利于添加到索引键。最后,由于OR
(也许您的意思是AND
?),无论索引如何,第二个查询都可能进行表扫描。
完全同意你所有的 cmets,我放在那里的例子只是为了强调 OP 提出的两种场景之间的差异,而没有涉及关于索引使用等的太多细节。谢谢你添加不过,如果有人看这个问题,额外的信息肯定会很有用:)【参考方案3】:
索引的内部存储采用 B-Tree 结构,由“索引页”(根页和所有中间页)和“索引数据页”(仅叶页)组成。
只有索引列存储在索引页上。 通过在注意不要将“索引数据页”与存储大部分实际数据列的“数据页”(聚集索引的叶页)混淆。
INCLUDE
部分中放置一些列,可以减少每个索引键在每个页面上存储的数据。
意味着需要更少的页面来保存索引键。 (更容易将这些经常使用的页面在内存中缓存更长时间。)
树中的级别可能更少。 (在这种情况下,性能优势可能会大得多,因为每个树级别的遍历都是另一个磁盘访问。)
使用索引时,索引键用于在索引页面之间导航到正确的索引数据页面。
如果索引有INCLUDE
列,则该数据在查询需要时立即可用。
如果查询需要在索引键或INCLUDE
列中不可用的列,则需要对聚集索引中的正确行进行额外的“书签查找”(如果未定义聚集索引,则需要堆)。
一些需要注意的事项,希望能解决您的一些困惑:
如果查询中的索引和过滤器的键选择性不够,则索引将被忽略(无论您的INCLUDE
列中有什么内容)。
您创建的每个索引都有 INSERT 和 UPDATE 语句的开销; “更大”的索引更是如此。 (更大的也适用于 INCLUDE
列。)
因此,尽管理论上您可以创建大量包含包含列的大索引来匹配访问路径的所有排列:但这会适得其反。
值得注意的是,在 INCLUDE
列作为功能添加之前:
INCLUDE
列基本上可以更有效地实现相同的好处。
注意需要指出的非常重要的一点。如果您习惯于总是将查询写为
SELECT * ...
,那么您通常从索引中的INCLUDE
列中获得零收益。通过返回所有列,您基本上可以确保在任何情况下都需要进行书签查找。
【讨论】:
我认为第一行非常具有误导性。没有什么叫index data pages
。非集群永远不会有data pages
。 Key
和 include column
都将仅存储在索引页面中
@Prdp 在撰写文章时,我确实对这个词进行了相当多的思考。我想区分普通索引页面和那些有额外包含列数据的页面。这就是我选择索引数据页的原因。
@Prdp 我希望编辑澄清你满意?
我知道这是旧的,但可以肯定地说使用 INCLUDE 选项(包含索引)而不是包含索引键(普通索引)中的所有列总是更好吗?
以上是关于包含列的索引,有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
mysql中,索引,主键,唯一索引,联合索引的区别是?对数据库的性能有啥影响?