聚集索引和非聚集索引

声明:本文中提到的索引都是以B+ tree组织的

MySQL的Innodb存储引擎的索引分为聚集索引和非聚集索引两大类, 理解聚集索引和非聚集索引可通过对比汉语字典的索引。 汉语字典提供了两类检索汉字的方式,第一类是拼音检索(前提是知道该汉字读音), 比如拼音为cheng的汉字排在拼音chang的汉字后面, 根据拼音找到对应汉字的页码(因为按拼音排序,二分查找很快就能定位),这就是我们通常所说的字典序; 第二类是部首笔画检索,根据笔画找到对应汉字,查到汉字对应的页码。 拼音检索就是聚集索引,因为存储的记录(数据库中是行数据、字典中是汉字的详情记录)是按照该索引排序的; 笔画索引,虽然笔画相同的字在笔画索引中相邻,但是实际存储页码却不相邻。

正文内容按照一个特定维度排序存储,这个特定的维度就是聚集索引;

Innodb存储引擎中行记录就是按照聚集索引维度顺序存储的,Innodb的表也称为索引表; 因为行记录只能按照一个维度进行排序,所以一张表只能有一个聚集索引。

非聚集索引索引项顺序存储,但索引项对应的内容却是随机存储的;

举个例子说明下:

create table student (

`id` INT UNSIGNED AUTO_INCREMENT,

`name` VARCHAR(255),

PRIMARY KEY(`id`),

KEY(`name`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

该表中主键id是该表的聚集索引、name为非聚集索引;表中的每行数据都是按照聚集索引id排序存储的; 比如要查找name='Arla'和name='Arle'的两个同学,他们在name索引表中位置可能是相邻的, 但是实际存储位置可能差的很远。name索引表节点按照name排序,检索的是每一行数据的主键。 聚集索引表按照主键id排序,检索的是每一行数据的真实内容。

也就是说查询name='Arle'的记录时,首相通过name索引表查找到Arle的主键id(可能有多个主键id,因为有重名的同学), 再根据主键id的聚集索引找到相应的行记录;

聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL的唯一索引,如果还是没有的话, 就采用Innodb存储引擎为每行数据内置的6字节ROWID作为聚集索引。

每张表只有一个聚集索引,因为聚集索引在精确查找和范围查找方面良好的性能表现(相比于普通索引和全表扫描), 聚集索引就显得弥足珍贵,聚集索引选择还是要慎重的(一般不会让没有语义的自增id充当聚集索引)。

从宏观上分析下聚集索引和普通索引的性能差异,还是针对上述student表:

(1)select * from student where id >5000 and id <20000;

(2)select * from student where name > 'Alie' and name < 'John';

第一条SQL语句根据id进行范围查询,因为(5000, 20000)范围内的记录在磁盘上按顺序存储, 顺序读取磁盘很快就能读到这批数据。

第二条SQL语句查询('Alie', 'John')范围内的记录,主键id分布可能是离散的1,100,20001,5000.....; 增加了随机读取数据页几率;所以普通索引的范围查询效率被聚集索引甩开几条街都不止; 非聚集索引的精确查询效率还是可以的,比聚集索引查询只增加了一次IO开销。

聚集索引表记录的排列顺序与索引的排列顺序一致,优点是查询速度快,因为一旦具有第一个索引值的纪录被找到,具有连续索引值的记录也一定物理的紧跟其后。

聚集索引的缺点是对表进行修改速度较慢,这是为了保持表中的记录的物理顺序与索引的顺序一致,而把记录插入到数据页的相应位置,必须在数据页中进行数据重排,降低了执行速度。

非聚集索引指定了表中记录的逻辑顺序,但记录的物理顺序和索引的顺序不一致, 聚集索引和非聚集索引都采用了B+树的结构, 但非聚集索引的叶子层并不与实际的数据页相重叠,而采用叶子层包含一个指向表中的记录在数据页中的指针的方式。 非聚集索引比聚集索引层次多,添加记录不会引起数据顺序的重组。

  • 聚集索引:物理存储按照索引排序
  • 非聚集索引:物理存储不按照索引排序

聚集索引在插入数据时速度要慢(时间花费在“物理存储的排序”上,也就是首先要找到位置然后插入),但查询数据比非聚集数据的速度快

索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。