做者 : Stanley 罗昊html
注:本文章须要MySQL数据库优化基础或观看前几篇文章,传送门:mysql
B树索引详讲(初识SQL优化,认识索引):https://www.cnblogs.com/StanleyBlogs/p/10413349.htmlsql
B树索引进阶(索引分类、建立方式、删除索引、查看索引、SQL性能问题):https://www.cnblogs.com/StanleyBlogs/p/10416865.html数据库
SQL执行计划于笛卡尔积(了解什么是SQL执行计划,优化原理):https://www.cnblogs.com/StanleyBlogs/p/10422202.html工具
Type详讲(理解优化级别):https://www.cnblogs.com/StanleyBlogs/p/10426385.htmlpost
Extra(理解最终优化概念):https://www.cnblogs.com/StanleyBlogs/p/10429969.html性能
首先咱们须要有一个数据库,bookdb,还要有一张数据表book,有如下字段,咱们接下来将用如下这张表来作优化实例;测试
这次教程再也不使用可视化工具,由于效率太慢,我仍是比较喜欢命令行操做;优化
下面咱们须要编写如下条件的SQL语句:spa
查询authorid = 1而且 typeid为2或3的bid再根据typeid排序
SQL语句:select bid from book where typeid in (2,3) And authorid = 1 order by typeid desc;
咱们执行这条SQL语句,而后在前面加上explatin查看sql执行计划:
咱们能够清楚的看到,查询级别是ALL,而且Useing where回表查询了而且后面还有一个Using filesort表示建立临时表了,可见此条SQL语句是多么的恐怖,效率极低;
这个时候咱们就来给它加个索引吧,毕竟都知道,加索引能够提升效率,那咱们就来分析一下以上这个sql语句;
首先该语句里面有 bid typeid authorid这三个字段,那么接下来我将给它们建立一个复合索引:
索引名我起名为idx_bta表明它的顺序 b 表明 bid t 表明 typeid a 就表明authorid;
加上索引后,咱们再执行一下,看看咱们这条sql语句有没有被优化:
首先,咱们能够看见,type级别被优化了一些,到了index了,也就是ALL的上一个级别,我在以前的文章也说过,最好优化到ref级别,可见咱们如今这条SQL仍是不够优化,而且 咱们后面还有Using filesirt,可是出现了Using index,说明仍是优化了一些,可是远远不够!
那么为何呢?我明明加了索引,它竟然还性能这么差?
原来,咱们忽略了一点,就是SQL解析过程!
我在前几篇文章重点说过,编写过程,解析过程是不同的:
select from join on where 条件 group by 分组 having过滤组 order by排序 limit限制查询个数
from on join where group by having select order by limit
以上就是mysql的解析过程,咱们发现,跟咱们编写的过程彻底不一致!
也就是说,咱们尽管bid在前面,typeid跟authorid在后面,可是它实际执行的时候倒是先执行where(type、authorid),而不是select(bid);
可是咱们索引顺序是怎么建的?
是 b t a 的顺序(bid typeid authorid),既然where我如今非要先让bid先执行,很显然不知足最佳左前缀,就是从左向右依次执行,我如今的索引并无知足,由于我如今却让最右边的先执行了(bid);
因此,咱们须要改变一下索引的顺序,既然先解析where,我就让where后面的俩字段放在前面(typeid authorid),把select放在后面(bid);
根据SQL实际解析的顺序,调整索引的顺序;
在创建这个索引以前,咱们务必删掉没用的索引!
删掉后,咱们把索引的顺序改变一下,以前是 b t a 如今我改为 t a b(typeid authorid bid);
添加索引后,我而且查询索引,发现建立成功了,咱们再运行一下试试,此次我改变了索引顺序,顺序按照解析顺序排列,看看此次的效果如何:
咱们发现,type等级仍然是index,由于没有建立临时表了也就是额外的查询,性能明显提高了,可是咱们的type等级还是index,确实尚未达到咱们想要的ref标准,接下来咱们继续优化;
咱们如今开始重点优化索引级别,很显然,咱们的索引级别是index,距离ref还有点距离;
system>const>eq_ref>ref>range>index>ALL
很明显啊,咱们如今的这条sql才到index级别,我以前说过,最好达到ref或range级别;
咱们来把以前的SQL拿过来:
select bid from book where typeid in (2,3) And authorid = 1 order by typeid desc;
我如今将where条件后面这两个字段换一个顺序,为啥换顺序呢,看这个in
我以前讲过范围查询,in是有可能致使索引失效的,从而转为无索引;
我如今思路是,若是in失效了或typeid失效了,那你authorid也就跟着一块儿失效了,为何呢?
咱们来看一下索引顺序,咱们是先typeid 后 authorid,若是你typeid都没了,那么authorid也可能也受干扰了,因此我把它顺序换换;
alter table book add index idx_atb(authorid,typeid,bid);
我如今让它先authorid后typeid,那若是先a 后 t 那便是你 t 失效了,无所谓啊,我先a,a你也用了,这是个思路;
既然typeid会失效,那咱们改变一下where后面的顺序吧,既然你可能会失效,就把它日后放,别影响别人:
explain select bid from book where authorid = 1 And typeid in (2,3) order by typeid desc;
务必也把索引顺序也更改一下!
alter table book add index idx_atb(authorid,typeid,bid);
咱们改变索引顺序跟SQL语句where后面的顺序后再执行:
咱们再查看type级别会发现已经达到了fef级别,而且也有Using index,可是还有Using where
由于我typeid虽然也有索引,可是我使用了in,就索引失效了,就跟typeid没有索引同样,这样就形成了又须要回原表查了,因此尽可能避免使用in;
小结:之因此这条SQL语句能达到了ref,是由于我知足了最佳左前缀,跟处理了索引失效的问题,既然你要失效,我就不要让你影响后面的字段,我就把你日后排,尽管你失效了,你不影响前面的,因此我把SQL语句where后面的两个字段换了位置;
索引须要逐步优化,要实时查询sql执行计划,采起逐步优化措施;
刚刚在上方我把SQL语句字段where后面的字段调换了位置,而且索引也跟着调换了,其实通过测试,其实SQL语句无需调换,仅需调换索引位置便可:
今日感悟:
在你看不见的地方,总有你想不到的辛酸;