一个导致查询结果错的MySQL bug
现象
有业务系统反应,通过主键条件查询用户表,得到的一行结果与条件中的主键值不一致,这是什么鬼啊?
结论
设置JDBC参数不合理(queryTimeout=10s,socketTimeout=10s),导致在异常情况下,第二条get获得了第一条get的结果,得到错误的数据,数据库表现正常;
详细的出错流程
(1) 应用设置queryTimeout=10s,socketTimeout=10s;
(2) 在JDBC代码中,socketTimeout是利用fd超时实现的,而queryTimeout是另外起一个任务(cancel线程),监听request是否完成,如果在queryTimeout内未完成,则新建一个连接发kill query,将主线程的query干掉;
(3) 执行流程中,queryTimeout先被schedule,socektTimeout后被enable到fd上,但由于这个两个timeout的时间相同,因此queryTimeout先生效;
(4) queryTimeout生效后,将发送kill query,希望将主线程的请求干掉;当时的情况是,单条get在server上执行很快,由于网络问题,回包在网络上fly了很长时间(10s+),因此实际上发送的那条kill query没有干任何事情;
(5) 而后socektTimeout生效,主线程获取异常(CommunicationsException),继续执行,但JDBC会check一下请求是否被cancel掉了,如果被cancel掉了就会抛出MySQLTimeoutException,原来的CommunicationsException被覆盖掉了;
(6) 最终应用得到的异常是MySQLTimeoutException,注意,此时请求的数据还在网络上fly;中间件对于CommunicationsException的异常会断连接,而MySQLTimeoutException异常认为是普通错误,将连接重新放回连接池中;
(7) 放回连接池的连接被应用其它线程获得,又发送一个get请求,等待接收response;
(8) 而此时第一个get请求的response到了,JDBC返回给应用,这样就收到了错误的数据;
网络问题
在出问题时间点附近,网络抖动比较厉害:
复现
在mysql(5.6.23)和JDBC(5.1.34)复现,利用iptables将网络包阻塞在网络中一定时间;
测试代码:
测试结果
建议
MySQL很好用,使用要慎重!没有踩坑的准备和填坑的能力,那就只能呵呵哒了!
文章转载自公众号:OceanBase