mysql索引优化原则:覆盖索引、最左前缀原则、索引下推
前言
在文章开始前,小编提出几个问题,读者可以思考一下如何回答。如果对于以下的问题,回答的模棱两可甚至根本不了解,我想这篇文章应该会合你的口味。
查询数据时如何减少回表的次数?
什么是“最左前缀原则”?解决了什么问题?
什么是“索引下推”?这种方法带来了什么好处?
round1:覆盖索引
查询数据时如何减少回表的次数?
回答这个问题前,我们要知道什么是回表?
我们都知道索引分为主键索引和非主键索。先来看看执行一条 sql,Mysql 是如何执行搜索操作的?
select * from T where k between 3 and 5
SQL执行流程:
1、在k索引树上找到索引k为3的记录,取得ID = 300;
2、再到ID索引树查询ID=300对应的R3;
3、在k索引树取下一个值k=5,取得ID=500;
4、再回到ID索引树查到ID=500对应的R4;
5、在k索引树取下一个值k=6,不满足条件,循环结束。
在这个执行过程中,回到主键索引树搜索的过程称为回表。可以看到,这个查询过程中读了k索引树的3条记录(分别是索引3,5,6),回表了两次(分别是300,500)。
在这个例子中,由于查询结果所需要的数据只在主键索引上有,所以不得不回表。那么,有没有可能经过索引优化,避免回表过程呢?
答案是肯定的,覆盖索引可以避免回表过程。如果我们执行的语句是:
select id from T where k between 3 and 5
此时,只需要查询 id 的值,而 id 的值已经在k索引树上了,因此,可以直接返回查询结果,避免了回表过程。也就是说:在这个查询里面,索引 k 已经 “覆盖了” 我们的查询请求,我们称为覆盖索引。
由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
round2:最左前缀原则
了解什么是最左前缀原则前,先来看看什么是联合索引?
两个或更多个列上的索引被称作联合索引,联合索引又叫复合索引。如:User 表的 name 和 age 加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或者几列,则此列就可以被匹配到。例如:
select * from user where name = xx and age = xx; # 可以命中索引
select * from user where name = xx; # 可以命中索引
select * from user where city = xx; # 无法命中索引
这里,要注意的是,查询的时候两个条件都用上了,但是顺序不同,如 city = xx and name = xx ,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
举例说明“最左前缀原则”的使用场景,现在要求根据市民的身份证号查询姓名,由于这种查询比较高频,所以可以建立(身份证号,姓名)的联合索引。但如果现在出现了新的需求,要求根据市民的身份证号查询家庭住址,但这种情况不算高频,但也不能全盘扫描,那新建联合索引?但又不是高频请求,新建联合索引是不是有些浪费?那应该怎么办???
当然是“最左前缀原则”派上用场了,还记得“最左前缀原则” 吗?如果查询条件可以精确匹配索引的左边连续一列或者几列,则此列就可以被匹配到。这样,利用(身份证号)去查询家庭住址,就可以解决上面的问题了。
基于上面对最左前缀索引的说明,我们来讨论一个问题:在建立联合索引的时候,如何安排索引内的字段顺序?
这里我们的评估标准是,索引的复用能力。因为可以支持最左前缀,所以当已经有了(a,b)这个联合索引后,一般就不需要单独在a上建立索引了。因此,第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。
round3:索引下推
上一段我们说到满足最左前缀原则的时候,最左前缀可以用于在索引中定位记录。这时,你可能要问,那些不符合最左前缀的部分,会怎么样呢?
我们以市民表的联合索引(name, age)为例。如果现在有一个需求:检索出表中“名字第一个字是张,而且年龄是10岁的所有男孩”。那么,SQL语句是这么写的:
select * from tuser where name like '张%' and age=10 and ismale=1;
现在,我们已经知道了 "最左前缀原则”,所以,在搜索索引树时,只能用“张”,找到第一个满足条件的记录ID3,总比全盘扫描要好。
然后呢?
我们需要对比其他条件是否满足。
在MySQL 5.6之前,只能从ID3开始一个个回表。到主键索引上找出数据行,再对比字段值。
而MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
例如,看看使用索引下推优化前的示意图:
使用索引下推优化后的示意图:
其中,每条虚线代表一次回表,两张图对比,很容易看出优化后的回表次数减少了。两种方式的区别是:InnoDB在(name,age)索引内部就判断了age是否等于10,对于不等于10的记录,直接判断并跳过。在我们的这个例子中,只需要对ID4、ID5这两条记录回表取数据判断,就只需要回表2次。
小结
本篇文章讲述了 “覆盖索引”、“最左前缀原则”、“索引下推”三种索引优化方式,其中“覆盖索引”的出现是为了减少索引回表次数,提高查询效率。“最左前缀原则”可以让我们更好的利用索引,知道了如何设计出更高效的索引。“索引下推” 是在回表前进行数据的判断,为了减少不能匹配最左前缀原则的数据查询的回表次数。
在满足语句需求的情况下, 尽量少地访问资源是数据库设计的重要原则之一。我们在使用数据库的时候,尤其是在设计表结构时,也要以减少资源消耗作为目标。