前言
上周新系统改版上线,上线次日就出现了较多的线上慢sql查询,紧接着dba 给出了定位及解决方案,这里较多的是使用延迟关联去优化。
而我对于这个延迟关联也是第一次据说(o(╥﹏╥)o),因此今天必定要学习并产出一篇学习笔记。(^▽^)
mysql
回表
咱们都知道InnoDB采用的B+ tree来实现索引的,索引又分为主键索引(聚簇索引)和普通索引(二级索引)。
那么咱们就来看下基于主键索引和普通索引的查询有什么区别?
sql
- 若是语句是select * from T where ID=500,即主键查询方式,则只须要搜索ID这棵B+树;
- 若是语句是select * from T where k=5,即普通索引查询方式,则须要先搜索k索引树,获得ID的值为500,再到ID索引树搜索一次。这个过程称为回表。
举个栗子:性能优化

能够看出咱们有一个普通索引k,那么两颗B+树的示意图以下:性能
(注:图来自极客时间专栏)
学习
当咱们查询 select * from T where k=5
其实会先到k那个索引树上查询k = 5,而后找到对应的id为500,最后回表到主键索引的索引树找返回所需数据。
若是咱们查询select id from T where k=5
则不须要回表就直接返回。
也就是说,基于非主键索引的查询须要多扫描一棵索引树。所以,咱们在应用中应该尽可能使用主键查询。
优化
覆盖索引
- 解释一: 就是select的数据列只用从索引中就可以取得,没必要从数据表中读取,换句话说查询列要被所使用的索引覆盖。
- 解释二: 索引是高效找到行的一个方法,当能经过检索索引就能够读取想要的数据,那就不须要再到数据表中读取行了。若是一个索引包含了(或覆盖了)知足查询语句中字段与条件的数据就叫作覆盖索引。
- 解释三:是非汇集组合索引的一种形式,它包括在查询里的Select、Join和Where子句用到的全部列(即创建索引的字段正好是覆盖查询语句[select子句]与查询条件[Where子句]中所涉及的字段,也即,索引包含了查询正在查找的全部数据)。
- 不是全部类型的索引均可以成为覆盖索引。覆盖索引必需要存储索引的列,而哈希索引、空间索引和全文索引等都不存储索引列的值,因此MySQL只能使用B-Tree索引作覆盖索引
- 当发起一个被索引覆盖的查询(也叫做索引覆盖查询)时,在EXPLAIN的Extra列能够看到“Using index”的信息
概念如上,这里咱们仍是用例子来讲明:spa

(注:图来自极客时间专栏)
如今,咱们一块儿来看看这条SQL查询语句的执行流程: select * from T where k between 3 and 5
code
- 在k索引树上找到k=3的记录,取得 ID = 300;
- 再到ID索引树查到ID=300对应的R3;
- 在k索引树取下一个值k=5,取得ID=500;
- 再回到ID索引树查到ID=500对应的R4;
- 在k索引树取下一个值k=6,不知足条件,循环结束。
在这个过程当中,回到主键索引树搜索的过程,咱们称为回表。能够看到,这个查询过程读了k索引树的3条记录(步骤一、3和5),回表了两次(步骤2和4)。
在这个例子中,因为查询结果所须要的数据只在主键索引上有,因此不得不回表。那么,有没有可能通过索引优化,避免回表过程呢?
blog
若是执行的语句是select ID from T where k between 3 and 5,这时只须要查ID的值,而ID的值已经在k索引树上了,所以能够直接提供查询结果,不须要回表。也就是说,在这个查询里面,索引k已经“覆盖了”咱们的查询需求,咱们称为覆盖索引。
因为覆盖索引能够减小树的搜索次数,显著提高查询性能,因此使用覆盖索引是一个经常使用的性能优化手段。
须要注意的是,在引擎内部使用覆盖索引在索引k上其实读了三个记录,R3~R5(对应的索引k上的记录项),可是对于MySQL的Server层来讲,它就是找引擎拿到了两条记录,所以MySQL认为扫描行数是2。
索引
延迟关联
上面介绍了那么多 实际上是在为延迟关联作铺垫,这里直接续上咱们本次慢查询的sql:
咱们都知道在作分页时会用到Limit关键字去筛选所需数据,limit接受1个或者2个参数,接受两个参数时第一个参数表示偏移量,即从哪一行开始取数据,第二个参数表示要取的行数。 若是只有一个参数,至关于偏移量为0。
当偏移量很大时,如limit 100000,10 取第100001-100010条记录,mysql会取出100010条记录而后将前100000条记录丢弃,这无疑是一种巨大的性能浪费。
当有这种写法时,咱们能够采用延迟关联来进行优化,重点关注:SELECT id FROM qa_question WHERE expert_id = 69 AND STATUS = 30 ORDER BY over_time DESC LIMIT 0, 10, 这里其实利用了索引覆盖,where条件后的expert_id 是有添加索引的,这里查询id 能够避免回表,大大提高效率。
结语
工做中会遇到各类各样的问题,对于一个研发来讲最重要的是可以从这些问题中学到什么。很久没有写博客了,究其缘由仍是本身变得懒惰了。 ( ̄ェ ̄;)
最后以《高性能Mysql》中的一段话结束: