覆盖索引的那些事儿

127 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

自己在找工作的时候,因为覆盖索引被面试官官怼过,现在就整理一些常问的问题。

什么是覆盖索引,为什么要用到覆盖索引呢?我想用MySQL为例来解释这些问题。

什么是覆盖索引呢?

了解覆盖索引之前先了解两个概念
聚集索引(clustered index):数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。
非聚集索引(secondary index):该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。

InnoDB聚集索引和非聚集有什么差异?

InnoDB聚集索引的叶子节点存储行记录,也就是整行的数据,因此, InnoDB必须要有,且只有一个聚集索引:

(1)如果表定义了主键,那么主键就是聚集索引;

(2)如果表没有定义主键,则第一个具有NOT NULL并且UNIQUE的列是聚集索引;

(3)不满足前两条件的,InnoDB会创建一个隐藏的row-id作为聚集索引;

  InnoDB非聚集的叶子节点存储主键值,也就是只有最多只有两个字段的数据,
  *不是存储行记录头指针,MyISAM的索引叶子节点存储记录指针。*

这个时候在引入覆盖索引,指从非聚集索引中就能获取到需要的记录,而不需要通过非聚集索引查找的主键值,再次去聚簇索引中查找记录(这个过程也叫回表)。使用覆盖索引的一个好处是因为非聚集索引不包括一条记录的整行信息,所以数据量比聚集索引要少,可以减少大量io操作。在MySQL里面,通过执行计划查看Extra是否使用了Using index,来判断是否触发覆盖索引。

+----+----------+------+------+
| id | name     | sex  | flag |
+----+----------+------+------+
|  1 | shenjian | m    | A    |
|  3 | zhangsan | m    | A    |
|  5 | lisi     | m    | A    |
|  9 | wangwu   | f    | B    |
+----+----------+------+------+
mysql> show index from t_demo;
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| t_demo |          0 | PRIMARY    |            1 | id          | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
| t_demo |          1 | idx_t_name |            1 | name        | A         |           4 |     NULL | NULL   | YES  | BTREE      |         |               |
+--------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
mysql> explain select id,name from t_demo where name = 'lisi';
+----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t_demo | NULL       | ref  | idx_t_name    | idx_t_name | 43      | const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------------+
mysql> explain select id,name,sex from t_demo where name = 'lisi';
+----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys | key        | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_demo | NULL       | ref  | idx_t_name    | idx_t_name | 43      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+---------------+------------+---------+-------+------+----------+-------+

看上面这个例子,表格t_demo有主键索引和name字段上的普通索引,通过name来进行查询id和name的数据,就只用通过name的索引树一次性查出不需要回表,如果要查看id,name,sex三个字段的信息,就需要先通过name的索引树查出主键id值,再通过主键的索引树获取需要的信息,这就产生了回表操作,效率低。

   覆盖索引就是从索引中直接获取查询结果,要使用覆盖索引需要注意select查询列中包含在索引列中;
   where条件包含索引列或者复合索引的前导列;
   查询结果的字段长度尽可能少。