最近没怎么搞SQL优化,碰巧数据库被慢查询搞挂了,因而拿来练练手。php
经过「SHOW FULL PROCESSLIST」语句很容易就能查到问题SQL,以下:mysql
SELECT post.* FROM post INNER JOIN post_tag ON post.id = post_tag.post_id WHERE post.status = 1 AND post_tag.tag_id = 123 ORDER BY post.created DESC LIMIT 100
说明:由于post和tag是多对多的关系,因此存在一个关联表post_tag。sql
试着用EXPLAIN查询一下SQL执行计划(篇幅所限,结果有删减):数据库
+----------+---------+-------+-----------------------------+ | table | key | rows | Extra | +----------+---------+-------+-----------------------------+ | post_tag | tag_id | 71220 | Using where; Using filesort | | post | PRIMARY | 1 | Using where | +----------+---------+-------+-----------------------------+
下面给出优化后的SQL,惟一的变化就是把链接方式改为了「STRAIGHT_JOIN」:app
SELECT post.* FROM post STRAIGHT_JOIN post_tag ON post.id = post_tag.post_id WHERE post.status = 1 AND post_tag.tag_id = 123 ORDER BY post.created DESC LIMIT 100
试着用EXPLAIN查询一下SQL执行计划(篇幅所限,结果有删减):post
http://www.gwdang.com/app/extension性能
+----------+----------------+--------+-------------+ | table | key | rows | Extra | +----------+----------------+--------+-------------+ | post | status_created | 119340 | Using where | | post_tag | post_id | 1 | Using where | +----------+----------------+--------+-------------+
对比优化先后两次EXPLAIN的结果来看,优化后的SQL虽然「rows」更大了,可是没有了「Using filesort」,综合来看,性能依然获得了提高。spa
提醒:注意两次EXPLAIN结果中各个表出现的前后顺序,稍后会解释。code
对第一条SQL而言,为何MySQL优化器选择了一个耗时的执行方案?对第二条SQL而言,为何把链接方式改为STRAIGHT_JOIN以后就提高了性能?
这一切还得从MySQL对多表链接的处理方式提及,首先MySQL优化器要肯定以谁为驱动表,也就是说以哪一个表为基准,在处理此类问题时,MySQL优化器采用了简单粗暴的解决方法:哪一个表的结果集小,就以哪一个表为驱动表,固然MySQL优化器实际的处理方式会复杂许多,具体能够参考:MySQL优化器如何选择索引和JOIN顺序。
说明:在EXPLAIN结果中,第一行出现的表就是驱动表。
继续post链接post_tag的例子,MySQL优化器有以下两个选择,分别是:
以post为驱动表,经过status_created索引过滤,结果集119340行
以post_tag为驱动表,经过tag_id索引过滤,结果集71220行
显而易见,post_tag过滤的结果集更小,因此MySQL优化器选择它做为驱动表,可悲催的是咱们还须要以post表中的created字段来排序,也就是说排序字段不在驱动表里,因而乎不可避免的出现了「Using filesort」,甚至「Using temporary」。
知道了前因后果,优化起来就容易了,要尽量的保证排序字段在驱动表中,因此必须以post为驱动表,因而乎必须借助「STRAIGHT_JOIN」强制链接顺序。
实际上在某些特殊状况里,排序字段能够不在驱动表里,好比驱动表结果集只有一行记录,而且在链接其它表时,索引除了链接字段,还包含了排序字段,此时链接表后,索引中的数据自己天然就是排好序的。
既然聊到这里顺带说点题外话,你们可能会遇到相似下面的问题:本来运行良好的查询语句,过了一段时间后,可能会忽然变得很糟糕。一个很大可能的缘由就是数据分布状况发生了变化,从而致使MySQL优化器对驱动表的选择发生了变化,进而出现索引失效的状况,因此没事最好多查查,关注一下这些状况。
…
对于「STRAIGHT_JOIN」,我总以为这种非标准的语法属于奇技淫巧的范畴,能不用尽可能不用,毕竟多数状况下,MySQL优化器都能作出正确的选择。