一次奇怪的mysql查询优化发现mysql查询优化器的bug

2 篇文章 0 订阅
1 篇文章 0 订阅

        最近刚上线的一个sql出现了慢查,经过分析发现了一个很有意思的问题,具体表现为,select * from tablename where a=xx and b=xx and c=xxx and time<xxx order by time desc; 这条sql的查询很快的,在60ms以内,但是如果再最后加上了limit 10,查询就会要2s多。当这个慢查被运维拉出来的时候,感觉非常不能理解。

        我先把表结构尽量简化一下。

CREATE TABLE `tablename` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT 
  `typeId` tinyint(1) unsigned NOT NULL DEFAULT '0' 
  `userId` int(11) unsigned NOT NULL COMMENT '用户id',
  `orderId` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '订单id',
  `tradeStatus` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '交易状态 0.交易中 1.交易关闭 2.交易成功',
  `otherStatus` tinyint(1) unsigned NOT NULL DEFAULT '0' ,
  `addTime` int(10) unsigned NOT NULL COMMENT '添加时间',
  PRIMARY KEY (`id`),
  KEY `IDX_tradeStatus_otherStatus_addTime` (`tradeStatus`,`otherStatus`,`addTime`) USING BTREE,
  KEY `IDX_tradeStatus_otherStatus_typeId_orderId` (`tradeStatus`,`otherStatus`,`typeId`,`orderId`),
  KEY `IDX_addTime` (`addTime`),
  KEY `IDX_userId_tradeStatus_addTime` (`userId`,`tradeStatus`,`addTime`) USING BTREE,
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='test'

        我最初的查询sql是

select *
  from tablename
 where userid= 123
   and tradestatus= 2
   and orderid= 0
   and addTime< 1566139866
order by addtime desc limit 10

        看了一下执行计划,当没有limit的时候,执行计划走了最后一条索引,执行计划的type是ref,rows是22800。当加上limit以后,执行计划只走了addTime的索引,并且type退化成了range,rows几乎是全表了。

        这是一个很诡异的事情,研究了几分钟后发现搞不定,决定先优化它,再研究问题。然后发现了两种解决方案。

方案一,将查询语句和limit语句分隔开:

select *
  from(
select *
  from tablename
 where userid= 3307619
   and tradestatus= 2
   and orderid= 0
   and addTime< 1566139866
order by addtime desc) tab limit 10

方案二,使用强制索引语句:

select *
  from tablename force index(IDX_userId_tradeStatus_addTime)
 where userid= 123
   and tradestatus= 2
   and orderid= 0
   and addTime< 1566139866
order by addtime desc limit 10

        处理完后发现这两个方案其实都是在强行使用了mysql更好的索引,并且第一个解决方案因为避开了limit反而使用了正确的索引,所以我怀疑有可能是mysql的查询优化器在遇到limit以后会失效。带着这个疑问又去查询资料,终于被我找到一篇帖子。

        https://yq.aliyun.com/articles/51065/

        这里把bug产生的原因贴出来,具体的内容大家可以自己去查看。

  • 第一阶段,优化器选择了索引 iabc,采用 range 访问;
  • 第二阶段,优化器试图进一步优化执行计划,使用 order by 的列访问,并清空了第一阶段的结果;
  • 第三阶段,优化器发现使用 order by 的列访问,代价比第一阶段的结果更大,但是第一阶段结果已经被清空了,无法还原,于是选择了代价较大的访问方式(index_scan),触发了bug。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值