浅聊 MySQL索引覆盖
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅聊 MySQL索引覆盖相关的知识,希望对你有一定的参考价值。
参考技术A 尽量使用覆盖索引,减少select *。 那么什么是覆盖索引呢? 覆盖索引是指 查询使用了索引,并 且需要返回的列,在该索引中已经全部能够找到 。现在有一张用户表tb_user;
索引情况:
接下来,我们来看一组SQL的执行计划,看看执行计划的差别,然后再来具体做一个解析。
Using where; Using Index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需 要回表查询数据
Using index condition:查找使用了索引,但是需要回表查询数据
因为,在tb_user表中有一个联合索引 idx_user_pro_age_sta,该索引关联了三个字段 profession、age、status,而这个索引也是一个二级索引,所以叶子节点下面挂的是这一行的主 键id。 所以当我们查询返回的数据在 id、profession、age、status 之中,则直接走二级索引 直接返回数据了。 如果超出这个范围,就需要拿到主键id,再去扫描聚集索引,再获取额外的数据了,这个过程就是回表。 而我们如果一直使用select * 查询返回所有字段值,很容易就会造成回表 查询(除非是根据主键查询,此时只会扫描聚集索引)。
为了大家更清楚的理解,什么是覆盖索引,什么是回表查询,我们一起再来看下面的这组SQL的执行过 程。
id是主键,是一个聚集索引。 name字段建立了普通索引,是一个二级索引(辅助索引)。
B. 执行SQL : select * from tb_user where id = 2;
根据id查询,直接走聚集索引查询,一次索引扫描,直接返回数据,性能高。
C. 执行SQL:selet id,name from tb_user where name = 'Arm';
虽然是根据name字段查询,查询二级索引,但是由于查询返回在字段为 id,name,在name的二级索 引中,这两个值都是可以直接获取到的,因为覆盖索引,所以不需要回表查询,性能高。
D. 执行SQL:selet id,name,gender from tb_user where name = 'Arm';
由于在name的二级索引中,不包含gender,所以,需要两次索引扫描,也就是需要回表查询,性能相 对较差一点。
五分钟告诉你什么是MySQL的覆盖索引
五分钟告诉你什么是MySQL的覆盖索引
前面我们已经对MySQL索引底层原理多少有一定的了解了,还不是很了解的小伙伴可以看我之前的博文:《不会吧不会吧,难道还有人不了解MySQL索引底层原理?》 和 《MySQL底层为什么要选用B+树作为索引的数据结构呢?》
其实在之前的博文中也提到了覆盖索引啦,然后今天就单独把这个拿出来讲一讲这个MySQL的性能优化: 覆盖索引。
覆盖索引
覆盖索引:SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。
首先要了解覆盖索引之前,你必须要了解什么是聚簇索引和非聚簇索引,对这个不了解的小伙伴可以看我之前的博文,还有一个必须要了解的就是回表,覆盖索引其实就是跟到底需不需要回表有直接的关系的。
什么是回表呢? 通俗的讲就是,如果索引的列在 select 所需获得的列中 或者根据一次索引查询就能获得记录就不需要回表,如果 select 所需获得列中有大量的非索引列,索引就需要到表中找到相应的列的信息,这就叫回表。只有非聚簇索引是需要回表的,所以如果你懂得非聚簇索引的存储的结构,你自然就知道为啥需要回表了。
注意:不是所有类型的索引都可以成为覆盖索引。覆盖索引必须要存储索引的列,而哈希索引、空间索引和全文索引等都不存储索引列的值,所以MySQL只能使用B-Tree索引做覆盖索引
我这里举一个例子你就可以很快明白了。
create table t1
(
a int primary key ,
b int,
c int,
d int,
e varchar(20)
)engine=InnoDB;
insert into t1 value (4, 3, 1, 1, 'd');
insert into t1 value (1, 1, 1, 1, 'a');
insert into t1 value (8, 8, 8, 8, 'h');
insert into t1 value (2, 2, 2, 2, 'b');
insert into t1 value (5, 2, 3, 5, 'e');
insert into t1 value (3, 3, 2, 2, 'c');
insert into t1 value (7, 4, 5, 5, 'g');
insert into t1 value (6, 6, 4, 4, 'f');
create index idx_t1_bcd on t1(b,c,d); -- 创建复合索引
接下来我们来看这些SQL,看看哪些SQL满足了覆盖索引。
explain select * from t1 where b = 1; -- 回表
explain select e from t1 where b = 1; -- 回表
explain select b from t1 where b = 1; -- 不用回表 Using index 覆盖索引
explain select b,c from t1 where b = 1; -- 不用回表 Using index 覆盖索引
explain select b,d from t1 where b = 1; -- 不用回表 Using index 覆盖索引
explain select b,c,d from t1 where b = 1; -- 不用回表 Using index 覆盖索引
explain select a,b,c,d from t1 where b = 1; -- 不用回表 Using index 覆盖索引
其实上面已经给出答案了。
我们通过执行计划就可以知道是不是满足了覆盖索引的条件了。
如果Extra使用了Using index,就说明了他是满足了覆盖索引了,这个就是覆盖索引的标志了。
而下面这种很明显就是不满足索引覆盖了。
因为我们建立的是复合索引,所以就是非聚簇索引,非聚簇索引的叶子节点上会存放键的值,也就是我们的(b,c,d) 这三个字段,还会存放主键a字段用于回表操作。
所以只要查询的列是你建立的索引字段再加上主键字段,都是满足索引覆盖的,这个时候我们在非聚簇索引的叶子节点就能给够获取到这些数据,不需要回表操作。
总结
如果要使用覆盖索引,一定要注意SELECT 列表值取出需要的列(并且这些列是有索引的),不可以是SELECT *,但有的人说可以全部列都加索引,但如果将所有字段一起做索引会导致索引文件过大,查询性能下降,不能为了利用覆盖索引而这么做。
参考
以上是关于浅聊 MySQL索引覆盖的主要内容,如果未能解决你的问题,请参考以下文章