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

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

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

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

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

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

        A表:session

        

       B表:测试

        

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

        A sql:3d

        

        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包不必定是马上返