前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >order by 主键id导致全表扫描的问题

order by 主键id导致全表扫描的问题

作者头像
用户1278550
发布2018-08-09 10:56:22
3.7K0
发布2018-08-09 10:56:22
举报
文章被收录于专栏:idbaidbaidba

一 简介 在检查某业务数据库的slowlog 时发现一个慢查询,查询时间 1.57s ,检查表结构 where条件字段存在正确的组合索引,正确的情况下优化器应该选择组合索引,而非为啥会导致慢查询呢? 且看本文慢慢分析。

二 分析 案例中的MySQL数据库版本 5.6.16 将生产环境的sql做适当修改,where条件不变。读者朋友可以测试一下其他的版本。

  1. root@rac1 10:48:11>explain select id,gmt_create, gmt_modified,order_id,service_id, seller_id,seller_nick, sale_type from lol where seller_id= 1501204 and service_id= 1 and sale_type in(3, 4) and use_status in(3, 4, 5, 6) and process_node_id= 6 order by id desc limit 0,20 \G
  2. *************************** 1. row ***************************
  3. id: 1
  4. select_type: SIMPLE
  5. table: lol
  6. type: index
  7. possible_keys: idx_sellerid,idx_usestatus_saletype,idx_sellerid_saletype,idx_sidustsvidtype
  8. key: PRIMARY --- 应该选择 idx_sidustsvidtype
  9. key_len: 8
  10. ref: NULL
  11. rows: 3076
  12. Extra: Using where
  13. 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, gmt_create,gmt_modified, order_id,service_id,seller_id,seller_nick, sale_type from lol force index(idx_sidustsvidtype)
  2. where seller_id= 1501204 and service_id= 1 and sale_type in(3, 4)
  3. and use_status in(3, 4, 5, 6)
  4. and process_node_id= 6 order by id desc limit 0,20 \G
  5. *************************** 1. row ***************************
  6. id: 1
  7. select_type: SIMPLE
  8. table: lol
  9. type: range
  10. possible_keys: idx_sidustsvidtype
  11. key: idx_sidustsvidtype
  12. key_len: 19
  13. ref: NULL
  14. rows: 5178
  15. Extra: Using where; Using filesort
  16. 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,gmt_create,gmt_modified,order_id,service_id,seller_id, seller_nick,sale_typefrom lol
  2. where seller_id= 1501204 and service_id= 1 and sale_type in(3, 4) and use_status in (3, 4, 5, 6) and process_node_id= 6 \G
  3. *************************** 1. row ***************************
  4. id: 1
  5. select_type: SIMPLE
  6. table: lol
  7. type: range
  8. possible_keys: idx_sellerid,idx_uts_stp,idx_sid_stpe,idx_sidustsvidtype
  9. key: idx_sidustsvidtype
  10. key_len: 19
  11. ref: NULL
  12. rows: 5178
  13. Extra: Using where
  14. 1 row in set (0.00 sec)

三 总结

  1. 修改SQL,添加正确hint,缺点是失去了sql的灵活性,遇到过索引修改导致带有hint的sql执行失败的案例,导致故障。
  2. 去掉不必要的order by 需要和开发沟通确认是否影响业务逻辑。
  3. 修改优化的bug,保留多个访问路径,不清理保存访问方式的quick变量,发现orderby 的代价高于组合索引时,可以选择最优的访问路径。
  4. 特别感谢 江疑 的分析,Bug 请参考原文链接。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-11-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档