官方定义
聚簇索引(Clustered Index)
聚簇索引:主键索引的 InnoDB 术语。InnoDB 表存储基于主键列的值进行组织,以加快涉及主键列的查询和排序。为了获得最佳性能,请根据最关键的性能查询仔细选择主键列。因为修改聚簇索引的列是一项昂贵的操作,所以选择很少或从不更新的主列。
参考:https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_clustered_index
每个 InnoDB 表都有一个称为聚簇(clustered index)索引的特殊索引,其中存储了行的数据。通常,聚簇索引与主键同义。为了从查询、插入和其他数据库操作中获得最佳性能,您必须了解 InnoDB 如何使用聚簇索引来优化每个表的最常见查找和 DML 操作。
- 当您在表上定义主键时,InnoDB 将其用作聚簇索引。为您创建的每个表定义主键。如果没有逻辑唯一和非
NULL
列或列集,请添加一个新的自动递增列,其值将自动填充。 - 如果您没有为表定义主键,MySQL 会找到第一个唯一索引,其中所有键列都不为
NULL
,InnoDB 将其用作聚簇索引。 - 如果表没有主键或合适的唯一索引,InnoDB内部会在一个包含行ID值的合成列上生成一个名为
GEN_CLUST_INDEX
的隐藏集群索引。行按 InnoDB 分配给此表中的行的 ID 排序。行 ID 是一个 6 字节的字段,随着新行的插入而单调增加。因此,由行 ID 排序的行在物理上按插入顺序排列。参考:https://dev.mysql.com/doc/refman/5.7/en/innodb-index-types.html
二级索引(Secondary Index)
二级索引:一种 InnoDB 索引,表示表列的子集。InnoDB 表可以有零个、一个或多个二级索引。
二级索引可用于满足仅需要索引列中的值的查询。对于更复杂的查询,可以使用它来标识表中的相关行,然后使用聚簇索引通过查找检索这些行。
创建和删除二级索引传统上涉及复制 InnoDB 表中的所有数据的重大开销。fast index creation功能使 InnoDB 二级索引的 CREATE INDEX
和 DROP INDE
X 语句都更快。
参考:https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_secondary_index
聚簇索引是怎样加速查询的
通过聚簇索引访问一行速度很快,因为索引搜索直接指向包含所有行数据的页面。如果表很大,与使用与索引记录不同的页面存储行数据的存储组织相比,聚集索引体系结构通常会节省磁盘I/O操作。
二级索引怎样与聚簇索引关联的
除聚簇索引之外的所有索引都称为二级索引。在 InnoDB 中,二级索引中的每个记录包含行的主键列,以及为二级索引指定的列。InnoDB 使用此主键值搜索聚簇索引中的行。
如果主键很长,二级索引就会使用更多的空间,所以有一个短的主键是有利的。
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开销。