写点什么

一篇文章讲清楚 MySQL 的聚簇 / 联合 / 覆盖索引、回表、索引下推

作者:一灯架构
  • 2022-10-20
    北京
  • 本文字数:1811 字

    阅读完需:约 1 分钟

迎面走来了你的面试官,身穿格子衫,挺着啤酒肚,发际线严重后移的中年男子。手拿泡着枸杞的保温杯,胳膊夹着 MacBook,MacBook 上还贴着公司标语:“加班使我快乐”。



面试官: 看你简历上用过 MySQL,问你几个简单的问题吧。什么是聚簇索引和非聚簇索引?


这个问题难不住我啊。来之前我看一下一灯 MySQL 八股文。


我: 举个例子:有这么一张用户表


CREATE TABLE `user` (  `id` int COMMENT '主键ID',  `name` varchar(10) COMMENT '姓名',  `age` int COMMENT '年龄',  PRIMARY KEY (`id`)) ENGINE=InnoDB CHARSET=utf8 COMMENT='用户表';
复制代码


用户表中存储了这些数据:



那么在索引中,这些数据是怎么存储的呢?


MySQL 的 InnoDB 引擎中索引使用的 B+树结构。



别问为什么根节点存储了(1,4)两个元素,左子节点又存储了(1,2,3)三个元素,下面带有三个叶子节点,叶子节点之间又用有序链表相连?


问就是 B+树的特性,不了解的可以翻一下上期的文章。


如上图所示,叶子节点中存储了全部元素的索引,就是聚簇索引。一般主键索引就是聚簇索引,如果表中没有主键,MySQL 也会默认建立一个隐藏主键做主键索引。


什么是非聚簇索引?


假设我们在 age(年龄)字段上建一个普通索引,age 字段上面的索引存储结构就是下面这样:



叶子节点中只存储了当前索引字段和主键 ID,这样的存储结构就是非聚簇索引。


面试官: 那什么是联合索引呢?


我: 有多个字段组成的索引就是联合索引。


面试官: 【晕】建联合索引有什么好处?它跟在单个字段上建索引有什么区别?


我: 假设有这么一条查询语句。


select * from user where age = 18 and name = '张三';
复制代码


如果我们在 age 和 name 字段上分别建两个索引,这个查询语句只会用到其中一个索引。


但是我们在 age 和 name 字段建一个联合索引(age,name),它的存储结构就变成这样了。



如果只在 age 上面建索引,会先查询 age 上面非聚簇索引,有三条 age=18 的记录,主键 ID 分别是 1、4、5,然后再用这三个 ID 去查询主键 ID 的聚簇索引。


如果在 age 和 name 上面建联合索引,会先查询 age 和 name 上面的非聚簇索引,匹配到一条记录,主键 ID 是 1,然后再用这个 ID 去查询主键 ID 的聚簇索引。


由此可以得出,联合索引的优点:大大减少扫描行数。


面试官: 你再说一下什么是最左匹配原则?


我: 最左匹配原则是指在建立联合索引的时候,遵循最左优先,以最左边的为起点任何连续的索引都能匹配上。


当我们在(age,name)上建立联合索引的时候,where 条件中只有 age 可以用到索引,同时有 age 和 name 也可以用到索引。但是只有 name 的时候是无法用到索引的。


为什么会出现这种情况呢?


看上面的图,就理解了,(age,name)的联合索引,是先按照 age 排序,age 相等的行再按照 name 排序。如果 where 条件只有一个 name,当然无法用到索引。


面试官: 什么是覆盖索引和回表查询?


我: 这个就更简单了,上面已经提到这个知识点了。



当我们在 age 上建索引的时候,查询 SQL 是这样的时候:


select id from user where age = 18;
复制代码


就会用到覆盖索引,因为 ID 字段我们使用 age 索引的时候已经查出来,不需要再二次回表查询了。


但是当查询 SQL 是这样的时候:


select * from user where age = 18;
复制代码


想要查询所有字段,就需要二次回表查询。因为我们第一次用 age 索引的时候只查出来了主键 ID,还需要再用主键 ID 回表查询出所有字段。


面试官: 再问一个,你知道什么是索引下推吗?


这么冷门的问题,你都问的出来,真的要面试造火箭啊!


我: 索引下推(Index Condition Pushdown)是 MySQL5.6 引入的一个优化索引的特性。


举例:


在(age,name)上面建联合索引,并且查询 SQL 是这样的时候:


select * from user where age = 18 and name = '张三';
复制代码



如果没有索引下推,会先匹配出 age = 18 的三条记录,再用 ID 回表查询,筛选出 name = '张三' 的记录。


如果使用索引下推,会先匹配出 age = 18 的三条记录,再筛选出 name = '张三' 的一条记录,最后再用 ID 回表查询。


由此得出,索引下推的优点:减少了回表的扫描行数。


**面试官: ** 小伙子,八股文背的挺溜啊。我给你出个实战题,看你有没有准备。下面这个查询 SQL 该怎么建联合索引?


select a from table where b = 1 and c = 2;
复制代码


故意刁难我?你以为实战题就不能背八股文了吗?


我: 刚才在讲联合索引的时候已经说了这个知识点了,where 条件有 b 和 c 的等值查询,联合索引就建成(b,c),由于 select 后面有 a,我们就建立 (b,c,a) 的联合索引,并且可以用到覆盖索引,查询速度更快。


面试官: 小伙子,有点东西。一会儿就给你发 offer,明天就来上班,薪资 double。


用户头像

一灯架构

关注

公众号:「一灯架构」 2020-11-20 加入

公众号:「一灯架构」,开发专家,只分享有趣的技术干货。

评论

发布
暂无评论
一篇文章讲清楚MySQL的聚簇/联合/覆盖索引、回表、索引下推_Java_一灯架构_InfoQ写作社区