覆盖索引
需要查询的值已经在索引里面包含了,就不需要查询数据行了。
mysql> explain select * from person1 where a like '%12%';
+----+-------------+---------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | person1 | NULL | ALL | NULL | NULL | NULL | NULL | 98999 | 11.11 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select a from person1 where a like '%12%';
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+-------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+-------+----------+--------------------------+
| 1 | SIMPLE | person1 | NULL | index | NULL | index_A_B_C | 21 | NULL | 98999 | 11.11 | Using where; Using index |
+----+-------------+---------+------------+-------+---------------+-------------+---------+------+-------+----------+--------------------------+
1 row in set, 1 warning (0.01 sec)
这个可能不是一个恰当的例子,但是事实上覆盖索引往往都要比非覆盖索引要高。
优点
- 索引的数据更加小,覆盖索引减少了回表,访问内存的数据比访问磁盘快的多
- 索引是以顺序存储的,让简单的范围查询直接使用索引的顺序
- 在mysiam中直接索引是基于内存实现的,数据则依赖操作系统的实现,减少调用操作系统能显著提升效率
- 在innodb中二级索引里面存了主键值,如果只查主键值和二级索引列的值,就可以减少一次去聚簇索引查询数据的IO
不是所有的值都能覆盖索引,比方说hash,空间索引,文本索引就不行,只有B-Tree索引才带数据
实战
mysql> explain select store_id,film_id from inventory;
+----+-------------+-----------+------------+-------+---------------+----------------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+----------------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | inventory | NULL | index | NULL | idx_store_id_film_id | 3 | NULL | 4581 | 100.00 | Using index |
+----+-------------+-----------+------------+-------+---------------+----------------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from inventory;
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | inventory | NULL | ALL | NULL | NULL | NULL | NULL | 4581 | 100.00 | NULL |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
可以发现只查索引字段时候 extra 中显示的是using index 但是select All的时候却没有使用索引了
mysql> explain select * from person_index where a = 12;
+----+-------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | person_index | NULL | ALL | index_a_b_c | NULL | NULL | NULL | 98999 | 10.00 | Using where |
+----+-------------+--------------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)
mysql> explain select a from person_index where a = 12;
+----+-------------+--------------+------------+-------+---------------+-------------+---------+------+-------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+-------+---------------+-------------+---------+------+-------+----------+--------------------------+
| 1 | SIMPLE | person_index | NULL | index | index_a_b_c | index_a_b_c | 21 | NULL | 98999 | 10.00 | Using where; Using index |
+----+-------------+--------------+------------+-------+---------------+-------------+---------+------+-------+----------+--------------------------+
1 row in set, 3 warnings (0.00 sec)
where条件里面有相关的字段,但是最终的查询结果字段集合里面还有其他的字段,那么也不会使用索引
ysql> explain select * from person_index inner join (select version from person where a =12) as t1 on t1.version = person_index.version;
+----+-------------+--------------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------------+
| 1 | SIMPLE | person | NULL | ref | PRIMARY,A | A | 4 | const | 1100 | 100.00 | Using index |
| 1 | SIMPLE | person_index | NULL | eq_ref | PRIMARY | PRIMARY | 22 | dev.person.version | 1 | 100.00 | NULL |
+----+-------------+--------------+------------+--------+---------------+---------+---------+--------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
先查处一部分数据再使用关联来做好歹也用一部分索引
以通配符开头的like
mysql> explain select * from tb_user where user_name like 'admin11%';
+----+-------------+---------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | range | PRIMARY | PRIMARY | 202 | NULL | 22436 | 100.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from tb_user where user_name like '%admin11%';
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | tb_user | NULL | ALL | NULL | NULL | NULL | NULL | 992136 | 11.11 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select user_name from tb_user where user_name like '%admin11%';
+----+-------------+---------+------------+-------+---------------+---------+---------+------+--------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+--------+----------+--------------------------+
| 1 | SIMPLE | tb_user | NULL | index | NULL | PRIMARY | 202 | NULL | 992136 | 11.11 | Using where; Using index |
+----+-------------+---------+-----------
这里和书上的不一样,覆盖索引现在支持了以通配符开头的查询
但是也不能迷信使用延迟关联的方式去查询,因为关联是有性能损耗的,在本身数据量就很小的情况下没有必要这样处理,反而会影响性能。
二级索引覆盖索引
因为在二级索引里面会带上主键的值所以在查询的时候可以利用这一点来做覆盖索引:
mysql> explain select * from person where a = 12;
+----+-------------+--------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+------+---------------+------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | person | NULL | ref | A | A | 4 | const | 1100 | 100.00 | Using index |
+----+-------------+--------+------------+------+---------------+------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> show create table person ;
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| person | CREATE TABLE `person` (
`A` int(50) NOT NULL,
`B` int(50) NOT NULL,
`C` int(50) NOT NULL,
`version` varchar(20) NOT NULL,
PRIMARY KEY (`version`),
KEY `A` (`A`,`B`,`C`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
person
里面总共就4个字段a,b,c是联合索引,version是主键,所以在使用a的联合索引的时候实际上就有这个4个字段,所以有 using index;