内容目录
概述
一个索引包含了所有需要查询的字段值,那么就称为覆盖索引。
好处
- 索引的大小通常远小于数据行大小,所以如果只需要读取索引,那么MySQL会极大的减少数据访问量。
- 索引是按照值得顺序存储的。
- InnoDB使用聚集索引(Cluster Index),InnoDB的二级索引叶子节点中保存了行的主键值,所以如果二级索引能够覆盖查询,那么可以避免对主键索引的二次查询。
覆盖索引并不限定必须是某种类型的索引,它指的是一种查询优化方式:如果查询可以完全由索引来完成,并不需要访问实际的行数据,那么这样的查询称为覆盖索引查询,这样的索引就称为覆盖索引。
原理
MySQL InnoDB所有二级索引都是指向聚集索引,而不是直接指向行记录,这样在记录更新时只需要更新聚集索引和记录的关联就可以了。但是在MyISAM存储引擎中,所有索引都直接指向行记录,所以在更新行记录时所有索引都需要更新。
但在MySQL InnoDB存储引擎中,查询使用二级索引可能需要会标查询,回表时查询聚集索引,这样就进行了二次查询造成多次IO。
如下图所示:
Explain覆盖索引提示
当查询使用了覆盖索引,可以在EXPLAIN的Extra列看到"Using index"的信息。
实验
基于MySQL 8.0.x
表结构和索引情况:
MySQL [employees]> desc sys_user;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | | NULL | |
| name_pinyin | varchar(255) | NO | | NULL | |
| id_card | varchar(255) | NO | UNI | NULL | |
| phone | varchar(20) | YES | MUL | NULL | |
+-------------+--------------+------+-----+---------+----------------+
5 rows in set (0.07 sec)
MySQL [employees]> show indexes from sys_user;
+----------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+----------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------------------+---------+------------+
| sys_user | 0 | PRIMARY | 1 | id | A | 3494 | NULL | NULL | | BTREE | | | YES | NULL |
| sys_user | 0 | uni_idx_id_card | 1 | id_card | A | 3494 | NULL | NULL | | BTREE | | 唯一索引-身份证号 | YES | NULL |
| sys_user | 1 | idx_phone_name | 1 | phone | A | 3493 | NULL | NULL | YES | BTREE | | 普通索引-手机号 | YES | NULL |
| sys_user | 1 | idx_phone_name | 2 | name | A | 3493 | NULL | NULL | | BTREE | | 普通索引-手机号 | YES | NULL |
+----------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------------------+---------+------------+
4 rows in set (1.54 sec)
覆盖索引查询
- 从sys_user表中查询手机号和姓名
explain select phone,name from sys_user;
EXPLAIN 输出结果:
MySQL [employees]> explain select phone,name from sys_user; +----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+ | 1 | SIMPLE | sys_user | NULL | index | NULL | idx_phone_name | 125 | NULL | 3494 | 100.00 | Using index | +----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
2. 从sys_user表中查询phone,name和id
```sql
explain select id, phone,name from sys_user;
EXPLAIN结果输出:
MySQL [employees]> explain select id, phone,name from sys_user;
+----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | sys_user | NULL | index | NULL | idx_phone_name | 125 | NULL | 3494 | 100.00 | Using index |
+----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
参考
- 高性能MySQL(第三版)