背景
本文是将网络上搜集到一些相关知识进行汇总,按着自己的理解和方式进行了重新的编辑。
1、MySQL 的索引
针对 MySQL 的索引,主要有以下几条:
- 它是一棵 B+Tree
- 每一个 B+Tree 的节点都是一个「数据页」
- 每一个「数据页」默认会占用 16KB 的磁盘空间
- 索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样
- 通过索引的查询是在存储引擎中完成的
在一棵 B+Tree 上会有很多数据页,上边我们也看到了每一个「数据页」会占用一定的磁盘空间,所以,如果大量的创建索引,势必会导致磁盘空间的消耗。
2、聚簇索引 && 非聚簇索引
首先什么是聚簇索引、非聚簇索引呢?简单的来说,如下:
- 聚簇索引:将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据(所有字段的值)。
- 非聚簇索引:将数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置。
看了上边的解释,可能对聚簇索引和非聚簇索引是什么还是有点摸不着通脑。别着急,下面分别以 MySQL 较为常用的两个存储引擎 InnoDB 和 MyISAM 为例,再展开说下。
在展开说明前,我们要先明确下两个概念:
- 主键索引:主键,一棵 B+Tree
- 辅助索引(二级索引):唯一索引、复合索引、前缀索引等等,一棵 B+Tree
2.1、InnoDB
在存储引擎为 InnoDB 的表中,主键索引的类型是聚簇索引,辅助索引的类型是都是非聚簇索引。结合上边对聚簇索引、非聚簇索引的定义,我们可以知道,InnoDB 的表中主键索引中的叶子点上存储了行数据(所有字段的值,而辅助索引叶子节点存储了索引列的值和主键值。
2.2、MyISAM
在存储引擎为 MyISAM 的表中,主键索引和辅助索引的类型都是非聚簇索引。两棵 B+Tree 的结构完全一致,只是存储的内容不同,主键索引 B+Tree 的节点存储了「主键」+「数据记录的地址」,辅助键索引 B+Tree 存储了「索引列的值」+「数据记录的地址」。还有一点不同是主键索引中的 key 必须是唯一的,而辅助索引中的 key 可以重复。
3、回表
主要发生在通过辅助索引查询的时候,通过辅助索引找到 B+Tree 中的叶子结点,但是辅助索引的叶子节点内存储的数据不全,只有索引列的值和主键值。我们还需要拿着刚从辅助索引中得到的主键值再去聚簇索引(主键索引)的叶子节点中去拿到完整的数据(全部字段),这个过程就叫「回表」。
下面介绍两个和回表操作相关的概念:索引覆盖 和 索引下推。
3.1、索引覆盖
如果非聚簇索引的叶子节点上有我们想要的返回的数据(字段),那就不需要回表了。例如:name 和 idcard 字段创建了一个联合索引(非聚簇索引),我们只想返回 主键、name、idcard 这 3 个字段(SELECT id, name, idcard WHERE name = “那XX”),因为 name、idcard 作为一个联合索引已经在辅助索引上的叶子节点上存有 name、idcard 的具体值,所以就不需要再回表操作了(除非 SELECT 里再增加一个 address 字段,这样就需要回表了)。
综上所述,这种索引中已经包含了所有需要读取的列数据的查询方式称为覆盖索引(或索引覆盖)。
3.2、索引下推(Index Condition Pushdown,ICP)
还是拿 name 和 idcard 的联合索引为例,我们要查询所有 name 为"那XX",并且 idcard 尾号为 5566 的记录,查询SQL如下:
SELECT * FROM t_user WHERE name = "那XX" AND idcard LIKE "%5566"
查询的过程:
- InnoDB 通过联合索引查出所有 name 为"那XX" 的辅助索引数据,假设得到 3 个主键值:3344,7687,46354。
- 拿到主键后,进行回表操作,到聚簇索引中拿到这 3 条完整的数据记录。
- InnoDB 把这 3 条完整的数据返回给 MySQL 的 Server 层,在 Server 层过滤出 idcard 尾号为 5566 的数据。
综上所述,索引下推,就是过滤的动作尽量由下层的存储引擎层通过使用索引来完成,而不需要上推到 Server 层进行处理。
4、参考
- 图解|用好MySQL索引,你需要知道的一些事情
- 聚簇索引和非聚簇索引
|