Mysql:好好的索引,为什么要下推?

前段时间有读者提议讲讲索引下推,这期就把这事儿安排上。多余的前言就不赘述了,我们直接开始。

列位坐好!

Mysql:好好的索引,为什么要下推?

图注:思维导图 

回表操作

对于数据库来说,只要涉及到索引,必然绕不过去回表操作。当然这也是我们今天所讲内容的前调基础。

说到回表,我们需要从索引开始说起。别担心,不会长篇大论,这里只是简单讲下主键索引与普通索引,目的是让大家对回表操作有个认识。如果你对回表操作很熟悉了,那么可以跳过这一段。

这里我们只以 Innodb 存储引擎作为讲解对象。

主键索引

主键索引在底层的数据存储是通过 B+ 树来实现的。简单来说,就是除叶子节之外的其他节点都存储的是主键值。而叶子节点上存储的是整行的数据。 

大体结构如下图所示。

Mysql:好好的索引,为什么要下推?

非主键索引

除了主键索引外,其它的索引都被称为非主键索引。与主键索引不同的是,非主键索引的叶子节点上存储的是主键的值。

Mysql:好好的索引,为什么要下推?

那让我们再回到开始的问题,什么是回表操作?

当我们在非主键索引上查找一行数据的时候,此时的查找方式是先搜索非主键索引树,拿到对应的主键值,再到主键索引树上查找对应的行数据。

这种操作就叫作回表操作。

好了,这里你应该了解了什么是回表操作了。简单来讲,就是在非主键索引树上拿到对应的主键值,然后回到主键索引上找到对应的行数据。

这样做的前提条件是,所要查找的字段不存在于非主键索引树上。

低版本操作

讲完了回表操作,让我们继续回到这篇文章的主题——索引下推。

其实在 Mysql 5.6 版本之前是没有索引下推这个功能的,从 5.6 版本后才加上了这个优化项。所以在引出索引下推前还是先回顾下没有这个功能时是怎样一种处理方式。

我们以一个真实例子来进行讲解。

在这里有张用户表 user,记录着用户的姓名,性别,身高,年龄等信息。表中 id 是自增主键,(name,sex) 是联合索引。在这里用 1 表示男,2 表示女。现在需要查找所有姓王的男性信息。

SQL 实现起来很简单:

Mysql:好好的索引,为什么要下推?

 

但是它的实现原理是什么呢?

根据联合索引最左前缀原则,我们在非主键索引树上找到第一个满足条件的值时,通过叶子节点记录的主键值再回到主键索引树上查找到对应的行数据,再对比是否为当前所要查找的性别。

整个原理可以用下边的图进行表示。

Mysql:好好的索引,为什么要下推?

看到了吧,低版本中需要每条数据都进行回表,增加了树的搜索次数。如果遇到所要查找的数据量很大的话,性能必然有所缺失。

高版本操作

讲完了低版本操作,让我们继续回到这篇文章的主题——索引下推。

知道了痛点,那么怎么解决。很简单,只有符合条件了再进行回表。结合我们的例子来说就是当满足了性别 sex = 1 了,再回表查找。这样原本可能需要进行回表查找 4 次,现在可能只需要 2 次就可以了。

Mysql:好好的索引,为什么要下推?

所以本质来说,索引下推就是只有符合条件再进行回表,对索引中包含的字段先进行判断,不符合条件的跳过。减少了不必要的回表操作。

总结

回表操作

  • 当所要查找的字段不在非主键索引树上时,需要通过叶子节点的主键值去主键索引上获取对应的行数据,这个过程称为回表操作。

索引下推

  • 索引下推主要是减少了不必要的回表操作。对于查找出来的数据,先过滤掉不符合条件的,其余的再去主键索引树上查找。

 

关于作者

作者:大家好,我是莱乌,BAT搬砖工一枚。从小公司进入大厂,一路走来收获良多,想将这些经验分享给有需要的人,因此创建了公众号【IT界农民工】。定时更新,希望能帮助到你。同时,我给大家肝了一份Redis面经手册,在我的公众号内回复【pdf】即可获取,希望对你有所帮助。

往期内容:

漫画系列:

漫画 | 程序员的悲哀是什么

入坑程序员,过来人给的一些小建议......

MySQL系列:

Mysql 中写操作时保驾护航的三兄弟!

MySQL中的这个池子,强的一批!

面试官:Mysql 中主库跑太快,从库追不上怎么整?

SQL:我为什么慢你心里没数吗?

Redis系列:

败家玩意儿!Redis 竟然浪费了这么多内存!

面试官:Redis 主从复制时网络开小差了怎么整?

同样是持久化,竟然有这么大的差别!

面试时说Redis是单线程的,被喷惨了!

硬核!15张图解Redis为什么这么快

缓存与库先写哪个,这十几张图告诉你!

 

Mysql:好好的索引,为什么要下推?

上一篇:谷歌浏览器插件抓取1688商品图片|文描|视频|规格|属性等信息 并打包下载


下一篇:使用NHibernate出现错误"此SqlParameterCollection 的 Count=** 的索引 ** 无效"