记一次mysql协议解析问题引发对索引的思考

原创
03/09 18:45
阅读数 5.1K

        近期负责的mysql mycat项目中接了一个需求,添加设置session级别的innodb_lock_wait_timeout,经过一番代码修改可以做到基本支持业务对for update的使用,但是到了测试环境发现出现个别sql会出现异常。

        具体现象是当在A表加for update排他锁时,业务通过mycat请求mysql查询加锁的表,会报超时异常。但是在B表加for update锁时,业务发起请求会卡住,并没有报超时异常,但是mycat日志显示数据库正常返回1205,且正常返回了。

        后面经过对比排查,发现时mycat源码bug(github上已被修复,我们使用的版本比较低),当数据库返回的包过大时,mycat会申请直接内存buffer用于存储数据,mycat使用完buffer回收Buffer时报空指针异常,导致线程挂掉,业务端接不到后续包所以卡住。

        本以为问题就这样结束,但是后续复盘时发现,两个sql均时返回同样的报错信息,为什么一个数据量大,一个数据量小呢?

        根据for update的原理,我们先查看下两张表的索引:

        A表:

        

       B表:

        

        再分析了下两条sql的执行计划:

        A sql:

        

        B sql:

        

        两条sql都是以row_id为条件,对比发现问题貌似出在索引上,A表row索引,B表使用的是联合索引。A sql执行计划是const常量级别的,Bsql 则是ref,即B sql是通过索引部分前缀,先找到符合多个条件的行,之后在确定唯一行的。 

        联合索引本质也是一个B+tree,不同的是联合索引键值数量不是1,而是大于等于二,当B sql执行时,会先去根据第一个键检索B+tree,找到的数据可能并不是一条,所以数据库会先回一部分正常返回包,当检索到被锁的数据后,才会回error包。

        

        再看mycat接收到的包(为了精简只打印了相关包类型):

        A sql :

        

        B sql:

        

        由此发现,B sql比A sql多收到一个fied包,所以B sql返回的数据要大于A sql,导致报错。

 

        之后又将B SQL改造,把其复合索引的两个条件都加上后执行计划和mycat日志如下:

        

        

        可见当type为const即根据索引找到的数据唯一时,mysql会立刻回Error包,但是若不唯一则可能会先回正常的result Set包,之后报错后再回error包。

        由此可见,工作中碰到的问题都是知识点的延申,经过本次问题的解决,在此记录相关知识点为:mysql复合索引的查找顺序

                                                                                                                                                                     mysql协议返回包时error包不一定是立刻返

        

展开阅读全文
打赏
3
2 收藏
分享
加载中
打赏
2 评论
2 收藏
3
分享
返回顶部
顶部