MySQL 聚集索引和非聚集索引

2021/05/30 MySQL 索引 聚集索引 非聚集索引 共 1548 字,约 5 分钟

聚集索引

聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一个聚集索引,因为一个表的物理顺序只有一种情况,所以,对应的聚集索引只能有一个。如果某索引不是聚集索引,则表中的行物理顺序与索引顺序不匹配,与非聚集索引相比,聚集索引有着更快的检索速度。

MySQL 聚集索引和非聚集索引

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 表:

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

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

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

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

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

文档信息

Search

    Table of Contents