汇集索引与非汇集索引的总结

一.索引简介

众所周知,索引是关系型数据库中给数据库表中一列或多列的值排序后的存储结构,SQL的主流索引结构有B+树以及Hash结构,汇集索引以及非汇集索引用的是B+树索引。这篇文章会总结SQL Server以及MySQL的InnoDB和MyISAM两种SQL的索引。html

SQL Sever索引类型有:惟一索引,主键索引,汇集索引,非汇集索引。

MySQL 索引类型有:惟一索引,主键(汇集)索引,非汇集索引,全文索引。
sql

二.汇集索引

汇集(clustered)索引,也叫聚簇索引。数据库

定义:数据行的物理顺序与列值(通常是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个汇集索引。性能

单单从定义来看是否是显得有点抽象,打个比方,一个表就像是咱们之前用的新华字典,汇集索引就像是拼音目录,而每一个字存放的页码就是咱们的数据物理地址,咱们若是要查询一个“哇”字,咱们只须要查询“哇”字对应在新华字典拼音目录对应的页码,就能够查询到对应的“哇”字所在的位置,而拼音目录对应的A-Z的字顺序,和新华字典实际存储的字的顺序A-Z也是同样的,若是咱们中文新出了一个字,拼音开头第一个是B,那么他插入的时候也要按照拼音目录顺序插入到A字的后面,如今用一个简单的示意图来大概说明一下在数据库中的样子:优化

地址 id username score
0x01 1 小明 90
0x02 2 小红 80
0x03 3 小华 92
.. .. .. ..
0xff 256 小英 70

注:第一列的地址表示该行数据在磁盘中的物理地址,后面三列才是咱们SQL里面用的表里的列,其中id是主键,创建了汇集索引。
指针

结合上面的表格就能够理解这句话了吧:数据行的物理顺序与列值的顺序相同,若是咱们查询id比较靠后的数据,那么这行数据的地址在磁盘中的物理地址也会比较靠后。并且因为物理排列方式与汇集索引的顺序相同,因此也就只能创建一个汇集索引了。code




汇集索引实际存放的示意图server

从上图能够看出汇集索引的好处了,索引的叶子节点就是对应的数据节点(MySQL的MyISAM除外,此存储引擎的汇集索引和非汇集索引只多了个惟一约束,其余没什么区别),能够直接获取到对应的所有列的数据,而非汇集索引在索引没有覆盖到对应的列的时候须要进行二次查询,后面会详细讲。所以在查询方面,汇集索引的速度每每会更占优点。htm

建立汇集索引

若是不建立索引,系统会自动建立一个隐含列做为表的汇集索引。blog

1.建立表的时候指定主键(注意:SQL Sever默认主键为汇集索引,也能够指定为非汇集索引,而MySQL里主键就是汇集索引)

create table t1(
    id int primary key,
    name nvarchar(255)
)

2.建立表后添加汇集索引

SQL Server

create clustered index clustered_index on table_name(colum_name)

MySQL

alter table table_name add primary key(colum_name)

值得注意的是,最好仍是在建立表的时候添加汇集索引,因为汇集索引的物理顺序上的特殊性,所以若是再在上面建立索引的时候会根据索引列的排序移动所有数据行上面的顺序,会很是地耗费时间以及性能。

三.非汇集索引

非汇集(unclustered)索引。

定义:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不一样,一个表中能够拥有多个非汇集索引。

其实按照定义,除了汇集索引之外的索引都是非汇集索引,只是人们想细分一下非汇集索引,分红普通索引,惟一索引,全文索引。若是非要把非汇集索引类比成现实生活中的东西,那么非汇集索引就像新华字典的偏旁字典,他结构顺序与实际存放顺序不必定一致。




非汇集索引实际存放的示意图

非汇集索引的二次查询问题

非汇集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块,此若是使用非汇集索引查询,而查询列中包含了其余该索引没有覆盖的列,那么他还要进行第二次的查询,查询节点上对应的数据行的数据。

若有如下表t1:

id username score
1 小明 90
2 小红 80
3 小华 92
.. .. ..
256 小英 70

以及汇集索引clustered index(id), 非汇集索引index(username)。

使用如下语句进行查询,不须要进行二次查询,直接就能够从非汇集索引的节点里面就能够获取到查询列的数据。

select id, username from t1 where username = '小明'
select username from t1 where username = '小明'

可是使用如下语句进行查询,就须要二次的查询去获取原数据行的score:

select username, score from t1 where username = '小明'

在SQL Server里面查询效率以下所示,Index Seek就是索引所花费的时间,Key Lookup就是二次查询所花费的时间。能够看的出二次查询所花费的查询开销占比很大,达到50%。



在SQL Server里面会对查询自动优化,选择适合的索引,所以若是在数据量不大的状况下,SQL Server颇有可能不会使用非汇集索引进行查询,而是使用汇集索引进行查询,即使须要扫描整个汇集索引,效率也比使用非汇集索引效率要高。



本人试过在含有30w行表上创建非汇集索引,查询非汇集索引覆盖之外的列就会变成汇集索引的全索引扫描(index scan)查询来避免二次查询,而在另一张200w行表才会用到非汇集索引seek对应的列再进行kek lookup,有关于SQL Server的有Index seek,index scan, table scan,key LookUp这几个概念,能够查看这个blog,描写比较详细。

但在MySQL里面就算表里数据量少且查询了非键列,也不会使用汇集索引去全索引扫描,但若是强制使用汇集索引去查询,性能反而比非汇集索引查询要差,这就是两种SQL的不一样之处。

还有一点要注意的是非汇集索引其实叶子节点除了会存储索引覆盖列的数据,也会存放汇集索引所覆盖的列数据。

如何解决非汇集索引的二次查询问题

复合索引(覆盖索引)

创建两列以上的索引,便可查询复合索引里的列的数据而不须要进行回表二次查询,如index(col1, col2),执行下面的语句

select col1, col2 from t1 where col1 = '213';

要注意使用复合索引须要知足最左侧索引的原则,也就是查询的时候若是where条件里面没有最左边的一到多列,索引就不会起做用。

在SQL Server中还有include的用法,能够把非汇集索引里包含的列包含进来,而不必定须要创建复合索引。

四.总结与使用心得

  1. 使用汇集索引的查询效率要比非汇集索引的效率要高,可是若是须要频繁去改变汇集索引的值,写入性能并不高,由于须要移动对应数据的物理位置。
  2. 非汇集索引在查询的时候能够的话就避免二次查询,这样性能会大幅提高。
  3. 不是全部的表都适合创建索引,只有数据量大表才适合创建索引,且创建在选择性高的列上面性能会更好。


另附本人博客地址

参考资料:

[1]:微软技术支持官方博客
[2]:汇集索引和非汇集索引(整理)

相关文章
相关标签/搜索