ITPub博客

【MySQL】全索引扫描的bug

原创 MySQL 作者:杨奇龙 时间:2015-11-05 10:59:16 0 删除 编辑
一 简介
  在检查某业务数据库的slowlog 时发现一个慢查询,查询时间 1.57s ,检查表结构 where条件字段存在正确的组合索引,正确的情况下优化器应该选择组合索引,而非为啥会导致慢查询呢? 且看本文慢慢分析。
二 分析
  案例中的MySQL数据库版本 5.6.16 将生产环境的sql做适当修改,where条件不变。读者朋友可以测试一下其他的版本。
  1. root@rac1 10:48:11>explain select id,
  2.     -> gmt_create,
  3.     -> gmt_modified,
  4.     -> order_id,
  5.     -> service_id,
  6.     -> seller_id,
  7.     -> seller_nick,
  8.     -> sale_type
  9.     -> from lol
  10.     -> where seller_id= 1501204
  11.     -> and service_id= 1
  12.     -> and sale_type in(3, 4)
  13.     -> and use_status in(3, 4, 5, 6)
  14.     -> and process_node_id= 6 order by id desc limit 0,20 \G
  15. *************************** 1. row ***************************
  16.            id: 1
  17.   select_type: SIMPLE
  18.         table: lol
  19.          type: index
  20. possible_keys: idx_sellerid,idx_usestatus_saletype,idx_sellerid_saletype,idx_sidustsvidtype
  21.           key: PRIMARY
  22.       key_len: 8
  23.           ref: NULL
  24.          rows: 3076
  25.         Extra: Using where
  26. 1 row in set (0.00 sec)
分析
MySQL选择的执行计划利用主键进行访问数据。注意执行计划中的 access type是index,而index 意味着这个SQL在查询二级索引的时候,对二级索引进行了全索引扫描,根本没有进行过滤
这个行为是不合理的,因为where条件中含有 in 查询,合理的执行计划的access type应该是range。
我们采用强制索引,看看结果

  1. root@rac1 10:48:07>explain select id,
  2.     -> gmt_create,
  3.     -> gmt_modified,
  4.     -> order_id,
  5.     -> service_id,
  6.     -> seller_id,
  7.     -> seller_nick,
  8.     -> sale_type
  9.     -> from lol force index(idx_sidustsvidtype)
  10.     -> where seller_id= 1501204
  11.     -> and service_id= 1
  12.     -> and sale_type in(3, 4)
  13.     -> and use_status in(3, 4, 5, 6)
  14.     -> and process_node_id= 6 order by id desc limit 0,20 \G
  15. *************************** 1. row ***************************
  16.            id: 1
  17.   select_type: SIMPLE
  18.         table: lol
  19.          type: range
  20. possible_keys: idx_sidustsvidtype
  21.           key: idx_sidustsvidtype
  22.       key_len: 19
  23.           ref: NULL
  24.          rows: 5178
  25.         Extra: Using where; Using filesort
  26. 1 row in set (0.00 sec)
分析
   强制加上索引之后的执行计划是符合预期的,执行sql的时间由 1.57s 减少为 0.01s 。因此我们推测是在优化器选择索引的时候出现了问题
结合源码和optimize_trace我们发现第一阶段优化的时候,优化器确实选择了idx_sidustsvidtype 并且选择采用range访问,因为sql 语句中含有order by,在optimizer试图优化 order by limit的时候
清空了保存访问方式的quick变量(原本保存的是range,但是被请空),最终发现采用排序索引(这里是id)的代价高于组合索引(这里是idx_sidustsvidtype)时,还是选择了idx_sidustsvidtype
但是悲剧的是这时候正确的访问方式已经被清空,无法还原,这就是这个 bug#78993  的根本成因。
根据分析,我们还可以使用另一种解决方法----去掉 order by 。当然这个对业务所有入侵必须和开发沟通确认sql的结果集是否唯一,如果不唯一还是要使用其他方法。

  1. root@rac1 10:48:15>explain select id,
  2.     -> gmt_create,
  3.     -> gmt_modified,
  4.     -> order_id,
  5.     -> service_id,
  6.     -> seller_id,
  7.     -> seller_nick,
  8.     -> sale_type
  9.     -> from lol
  10.     -> where seller_id= 1501204
  11.     -> and service_id= 1
  12.     -> and sale_type in(3, 4)
  13.     -> and use_status in(3, 4, 5, 6)
  14.     -> and process_node_id= 6 \G
  15. *************************** 1. row ***************************
  16.            id: 1
  17.   select_type: SIMPLE
  18.         table: lol
  19.          type: range
  20. possible_keys: idx_sellerid,idx_uts_stp,idx_sid_stpe,idx_sidustsvidtype
  21.           key: idx_sidustsvidtype
  22.       key_len: 19
  23.           ref: NULL
  24.          rows: 5178
  25.         Extra: Using where
  26. 1 row in set (0.00 sec)
三 总结 
a 修改SQL,添加正确hint。
b 去掉不必要的order by 需要和开发沟通确认是否影响业务逻辑。
c 修改优化的bug,保留多个访问路径,不清理保存访问方式的quick变量,发现orderby 的代价高于组合索引时,可以选择最优的访问路径。
特别感谢 江疑 的分析,同时也推荐排序sql升级5.6变慢原因分析  

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/22664653/viewspace-1823030/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
MySQL DBA NoSQL DEVOPS

注册时间:2009-10-07

  • 博文量
    1026
  • 访问量
    7457981