Sql Server关于create index include带有包含列的索引的最全解释

官方文档
https://docs.microsoft.com/zh-cn/sql/relational-databases/indexes/create-indexes-with-included-columns?view=sql-server-2017
https://docs.microsoft.com/en-us/sql/relational-databases/indexes/create-indexes-with-included-columns?view=sql-server-2017

在SqlServer 2016中,当我们要给一个长度超过1700的列创建索引时,数据库会告诉我们为这个列建索引会超过限制,而include的列是不在这个限制里面的。

create table t1(hid int,hname varchar(4000),hname1 varchar(4000))
create index ind_hname on t1(hname) --警告Warning! The maximum key length for a nonclustered index is 1700 bytes. The index 'ind_hname' has maximum length of 4000 bytes. For some combination of large values, the insert/update operation will fail.
create index ind_hname on t1(hid) include(hname) --不报错,正常创建,索引键列是hid,包含列是hname

官方文档的说法:通过包含非键列,可以创建覆盖更多查询的非聚集索引。 这是因为非键列具有下列优点:
1、它们可以是不允许作为索引键列的数据类型。
2、在计算索引键列数或索引键大小时,数据库引擎不考虑它们。
当查询中的所有列都作为键列或非键列包含在索引中时,带有包含性非键列的索引可以显著提高查询性能。 这样可以实现性能提升,因为查询优化器可以在索引中找到所有列值;不访问表或聚集索引数据,从而减少磁盘 I/O 操作。

以上2点的个人理解:
1、它们可以是不允许作为索引键列的数据类型。比如varchar(2000)超过1700不能做索引键列,但是varchar(2000)可以作为包含列
2、index key size索引键大小是指索引键包含的所有字段的长度总和的限制,比如我有一个表create table table1 (col1 varchar(500), col2 varchar(500),col3 varchar(500), col4 varchar(500));因为indexed key size的限制是1700, 所以create index ind_t1 on table1(col1, col2,col3)没有问题,因为col1+col2+col3=1500<1700,但是create index ind_t2 on table1(col1, col2,col3,col4)有问题,因为col1+col2+col3+col4=2000>1700,但是我们可以这样create index ind_t3 on table1(col1, col2,col3) include (col4),即把col4放入包含列,它就不占索引键ind_t3的大小

结合以上2点的理解,得出结论
1、include包含列只能是针对非聚集索引
2、Index space<>indexed key size+include columns size, Index space是整个index段的尺寸包括include列,Index space就是整个索引占用的磁盘空间,它包括索引键和非索引键,而不是说非索引键(包含列)就不占索引段的磁盘空间
3、当查询中的所有列就包含于索引的键值中,那么就不会发生Lookup回表的操作了,因为找到索引项,就已经找到所需的数据了,没有必要再到数据行去找了,这种情况,叫做索引覆盖
4、索引定义中非键列(包含列)的顺序不会影响使用该索引的查询的性能。
5、突然多出来一个查询,而这个查询字段只是比之前的查询多一个字段的情况下,如果之前的查询已经有了索引,则可以把之前索引删除,新建一个索引,再把新增的查询字段和之前索引中include字段合并起来一并放入include
6、当查询中的所有列都出现在索引(键列或包含列)中时,这种包含列的索引才能带来效果,也就是说它唯一的好处就是直接查询索引就可以返回值,不用回表,使用的范围太窄了。一旦要通过索引再回表返回数据,那包含列太得不偿失,因为包含列也占用实际的存储空间,这样导致这个索引的index space太大了,成本太高了,特别是超大表,索引如果包含太多包含列的话,占用存储实在太可怕了。所以include包含列是典型的空间换时间的打法,需要平衡场景使用,而不是说它一定很好

以下7、8两点结论,额外夹带的私活,帮忙理解索引扫描的原理
7、100GB的表,10个字段,每个字段平均10GB,每个字段建立一个索引,平均每个索引10GB,扫描某个索引时,不是直接扫描整个索引这10GB,而是扫描索引树根节点+索引树干节点+索引叶子节点,这样下来找到对应的索引键值,可能只需要扫描不到10MB的索引,如果要回表,那再加上回表的成本
8、大表创建索引,加上online=on不会影响业务,但是会产生巨大的事务日志,所以一般留到业务低峰比如周末操作

结论2的例子:
20210526公司发生的遇到的现象,csdb5的磁盘一下子被使用了200GB
如下语句,ATickMinutesArchive表大小254GB总计10个字段,原来的索引180GB,如下索引一创建,发现索引达到了400GB,也就是如下新建索引有200GB,这个索引大小刚好和表大小一致,说明include的8个字段和索引键的2个字段同时计算了到了索引大小中

CREATE NONCLUSTERED INDEX NIX_ATickMinutesArchive_001  
ON [dbo].[ATickMinutesArchive] ([Symbol],[DateUpdate])  
INCLUDE ([Last],[High],[Low],[TotalVol],[TimeUpdate],[Ticks],[OpenPrice],[OpenVolume])  
WITH ( ONLINE= ON )

结论3的例子1:
例如,taable1表上有col1列(主键),col2列,col3列,col4列,col5列…
现在要求作以下查询 select col2,col3 from taable1 where col2=@col2
一般来说,在col2列上加非聚集索引,就可以加速此查询。其查询步骤:检索col2列上的非聚集索引,获取对应主键col1,再由主键检索聚集索引,从聚集索引的叶级索引页上获取相关的记录。但是,如果在col2列上加非聚集索引时指定include col3列,这样col3列值便会存储在非聚集索引的叶级索引页上。其查询步骤:检索col2列上的非聚集索引,直接从非聚集索引的叶级索引页上获取相关记录。

结论3的例子2:
如遇到如下查询,则最后一个index_5都可以让下面这些所有查询语句只访问索引就返回数据,也就是说当查询中的所有列都作为键列或非键列包含在索引中时,直接查询索引就可以返回结果

select col1,col2 from table1 where col1=XX
select col1,col2,col3 from table1 where col1=XX
select col1,col2,col3,col4 from table1 where col1=XX
select col1,col2,col3,col4,col5 from table1 where col1=XX
select col1,col2,col3,col4,col5,col6  from table1 where col1=XX
CREATE INDEX [index_5] ON [dbo].[table1] ([col1]) INCLUDE ([col2], [col3], [col4], [col5], [col6]) WITH (DATA_COMPRESSION = PAGE, ONLINE = ON)

结论4的例子:
索引定义中非键列的顺序不会影响使用该索引的查询的性能,比如index_5和index_6效果一样, include后面的col2和col5顺序可以随意,可以col2在前,也可以col5在前

CREATE INDEX [index_5] ON [dbo].[table1] ([col1]) INCLUDE ([col2], [col3], [col4], [col5], [col6]) WITH (DATA_COMPRESSION = PAGE, ONLINE = ON)
CREATE INDEX [index_6] ON [dbo].[table1] ([col1]) INCLUDE ([col5], [col3], [col2], [col4], [col6]) WITH (DATA_COMPRESSION = PAGE, ONLINE = ON)

结论5的例子:
如遇到如下查询,且已经存在index_4索引,则可以把index_4删除,新建index_5索引,再把新增的查询col6和之前索引中include字段合并起来一并放入include

select col1,col2,col3,col4,col5,col6  from table1 where col1=XX
CREATE INDEX [index_4] ON [dbo].[table1] ([col1]) INCLUDE ([col2], [col3], [col4], [col5]) WITH (DATA_COMPRESSION = PAGE, ONLINE = ON)
CREATE INDEX [index_5] ON [dbo].[table1] ([col1]) INCLUDE ([col2], [col3], [col4], [col5], [col6]) WITH (DATA_COMPRESSION = PAGE, ONLINE = ON)

おすすめ

転載: blog.csdn.net/lusklusklusk/article/details/117419773