MySQL慢查询分析案例

   随着业务量的增长,运营同事反馈有个报表页面越来越慢,从对应的报表语句中逐个子查询筛查,找出如下最慢的语句:

   可以看到,其中有个子集全表扫了300多万行数据。抽出来单独explain,定位到问题语句:

SELECT t1.statDate, t1.tips
      FROM passport_4366_test t1 
      WHERE t1.statDate>='2016-05-01'
        AND t1.statDate<='2016-05-05'
      GROUP BY t1.statDate, t1.tips;

   检查一下表结构发现,在表的statDate上明明建立了索引,照理说type应该是range才对,怎么会是all呢?

   去掉group by语句试试:


   仍然是全表扫描。去掉一个字段再试试:


   要扫描的数据量变成1/5,type也变成range了。那么到底是什么原因导致MySQL查询执行器放弃使用索引直接进行全表扫描呢?去问问谷歌,搜索到如下可能有帮助的文章:



   回顾一下,这个表字段是date类型,难道是因为这个表同一天产生太多数据降低了索引区分度致使查询执行器放弃治疗?那么,把这个字段改成datetime类型会不会好点?好,我们来动手试试看。 

create table passport_4366_test like passport_4366;
insert into passport_4366_test select * from passport_4366 where statDate>='2016-04-01' AND statDate<='2016-05-06';
alter table passport_4366_test add index dt_idx(updateTime);
alter table passport_4366_test drop index statDate;

   然后explain看看:

   真是顽固不化的MySQL,只好force index了:


   终于乖乖听话了。想起来还有一个解决思路,那就是建立联合索引:

alter table passport_4366_test add index statDate_tips_idx(statDate, tips);

   再来看看explain的结果:

   效果很不错,rows列比force index的结果还要好(可能是因为Index Condition Pushdown Optimization)。拉大时间范围,直接执行语句对比一下执行时间:


   结果是联合索引完胜。

   最后,总结一下:

1. 建立索引时候尽量选择区分度大的列;

2. 适当条件下可以考虑建立联合索引,譬如本例中联合索引的优势明显;

3. 写SQL的时候要多点explain,主要看type和rows,一般来说,执行效率const > eq_ref > ref > range > all,rows值越少越好。

   参考文章:

http://blog.csdn.net/mchdba/article/details/9190771

https://www.percona.com/blog/2014/01/03/multiple-column-index-vs-multiple-indexes-with-mysql-56/

http://dev.mysql.com/doc/refman/5.6/en/index-condition-pushdown-optimization.html

http://tech.meituan.com/mysql-index.html

http://dba.stackexchange.com/questions/19400/why-does-mysql-only-sometimes-use-my-index-for-a-range-query

猜你喜欢

转载自lixuanbin.iteye.com/blog/2296842