mysql 中 in 一个值和多余一个值 执行计划不同导致的性能差异

         由于之前接触mysql比较少,现在工作环境mysql用的比较多些,对于做数据的我们来说,性能优化肯定是必不可少的,所以开了这个优化的分类。

               第一个优化说一说mysql中in一个值和多余一个值得优化。        


       现状:

 -- 多余一个值
 SELECT
 *
 FROM t1
 JOIN t2
 JOIN t3
 WHERE t3.areaCode IN ("420000","421023","410000")
 and t2 = XXX
 -- 一个值
 SELECT
 *
 FROM t1
 JOIN t2
 JOIN t3
 WHERE t3.areaCode IN ("420000")
 and t2 = XXX

       执行计划:in多个值时耗时0.14s:

       in一个值时耗时0.55s:

       通过以上我们可以看到执行计划的差别,主要在于t3表的执行计划不同


      优化思路:

      1、首先想到的是两个sql写法差异不大,可能是统计信息不准引起的执行计划不准,通过查询mysql的统计信息information_schema.tables 其实都是最新的,所以否掉该思路

       2、着重关注写法in一个值和多个值在执行计划上的差异

in后有一个值:有3种情况 对于主键或者唯一索引,那么type=const,这种性能最高,表示表中只有1个记录能满足查询;对于普通索引、或者联合主键,type=ref;对于普通字段,type=all,这种性能最差

in后有多个值:这时,不管是主键,还是唯一索引,还是普通索引,type=range,并且得考虑in的值得分布情况和in个数的占比

该例子执行计划符合ref 和all 的情况,但性能却大不相同,那么为什么多个值还会更快呢?

       3、再分析filtered的区别理论上来说where是会减小filtered的值,但是该执行计划只有t3有区别,t2却没有区别,经查询,条件过滤百分比,但却不是真实值,t2未过滤所以t3不可信

       4、关键点就在extra上:多个值使用了

Using join buffer (Block Nested Loop)

            经查询:BNL算法原理:将外层循环的行/结果集存入join buffer,内存循环的每一行数据与整个buffer中的记录做比较,可以减少内层循环的扫描次数,因此可以猜想bnl是比普通索引快的。如何才能让in一个值也走BNL呢。所以找到了 force index(t3索引),可强制走索引并查询缓存

 SELECT
 *
 FROM t1
 JOIN t2
 JOIN t3 FORCE INDEX(index)
 WHERE t3.areaCode IN ("420000","421023","410000") -- IN ("420000")
 and t2 = XXX

           执行计划均为,并且耗时0.055s,相较未加强制索引之前还快了1倍,较in一个值快了10倍:


           引申:mysql性能优化没有绝对的正确,如system/const/eq_ref/ref/range/index/all 顺序也不是绝对的快,所以执行计划也不是绝对的准确(之前一直被filtered误导),但是参考是必要的。该列子使用强制索引也是第一次遇到,但效果很好(有点强制主表的意思,t3表其实数据量是相对较小的)。另外在mysql机制当中恰当的使用索引是非常必要的,这个在后续的优化当中可能会详细聊到。

 

 

你可能感兴趣的:(mysql性能优化)