索引(Index)是帮助数据库高效获取数据的数据结构。索引是在基于数据库表创建的,它包含一个表中某些列的值以及记录对应的地址,并且把这些值存储在一个数据结构中。最常见的就是使用哈希表、B+树作为索引。
什么是聚集索引和非聚集索引?
Mysql 底层是用 B+ 树来存储索引的,且数据都存在叶子节点。对于 InnoDB 来说,它的主键索引和行记录是存储在一起的,因此叫做聚集索引(clustered index)。
MyISAM 的行记录是单独存储的,不和索引在一起,因此 MyISAM也就没有聚集索引。
除了聚集索引,其它索引都叫做非聚集索引(secondary index)。包括普通索引,唯一索引等。
唯一索引:如果确定某个数据列只包含彼此各不相同的值,在为这个数据列创建索引的时候,就应该用关键字UNIQUE把它定义为一个唯一索引,Mysql会在有新纪录插入数据表时,自动检查新纪录的这个字段的值是否已经在某个记录的这个字段里出现过了。如果是,mysql将拒绝插入那条新纪录。也就是说,唯一索引可以保证数据记录的唯一性。
另外需要注意,在 InnoDB 中有且只有一个聚集索引。它有三种情况:
若表存在主键,则主键索引就是聚集索引。
若不存在主键,则会把第一个非空的唯一索引作为聚集索引。
否则,就会隐式的定义一个 rowid 作为聚集索引。
为了方便理解,下边以 InnoDB 的主键索引和普通索引为例,看下它们的存储结构
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_stu` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
insert into student(id,name,age) values(1,'zs',12);
insert into student(id,name,age) values(5,'ls',14);
insert into student(id,name,age) values(9,'ww',12);
insert into student(id,name,age) values(11,'sq',13);
在 InnoDB 中,主键索引的叶子节点存储的是主键和行记录,而普通索引的叶子节点存储的是主键(对于 MyISAM来说主键索引的叶子节点存储的是主键和对应行记录的指针,普通索引的叶子节点存储的是当前索引列和对应行记录的指针)。
因此,id 为聚集索引,name 为非聚集索引。它们对应的 B+ 树结构如下图所示,
回表查询
从上边的索引存储结构,我们可以看到,在主键索引树上,通过主键就可以一次性查出来我们所需要的数据,速度非常的快。
因为主键和行记录就存储在一起,定位到了主键,也就定位到了所要找的记录,当前行的所有字段都在这(这也是为什么我们说,在创建表的时候,最好是创建一个主键,查询时也尽量用主键来查询)。
对于普通索引,如例子中的 name,则需要根据 name 的索引树(非聚集索引)找到叶子节点对应的主键,然后再通过主键去主键索引树查询一遍,才可以得到要找的记录。这就叫 回表查询。
以如下 sql 为例。
select * from student where name='zs';
它需要查询两遍索引树。
-
通过非聚集索引定位到主键 id=1。
-
通过聚集索引定位到主键id为1,对应的行记录。
它的查询过程图如下,
索引覆盖
索引覆盖,就是在用这个索引查询时,使它的索引树,查询到的叶子节点上的数据可以覆盖到你查询的所有字段,这样就可以避免回表。
还是以上边的表为例,现在 zs 对应的索引树上边,只有它本身和主键的数据,并不能覆盖到 age 字段。那么,我们就可以创建联合索引,如 KEY(name,age)。并且,查询的时候,显式的写出联合索引对应的字段(name和age)。
创建联合索引如下,
KEY `idx_stu` (`name`,`age`)
查询语句修改如下,
-- 覆盖联合索引中的字段
select id,name,age from student where name='zs' and age=12;
这样,当查询索引树的时候,就不用回表,可以一次性查出所有的字段。对应的索引树结构如下:
图中,联合索引中的字段(name,age)都应该出现在索引树上的,这里为了画图方便,且因数据量太小,没有画出来。只表现出了:叶子节点存储了所有的联合索引字段。
参考来源:https://segmentfault.com/a/1190000022690969