一个 MySQL-JDBC 驱动 bug 引起的血案

阅读数:74 2019 年 10 月 21 日 12:05

一个MySQL-JDBC驱动bug引起的血案

问题背景

公司是做电商系统的,整个系统搭建在华为云上。系统设计的时候,考虑到后续的用户和订单数量比较大,需要使用一些大数据库的组件。关系型数据库这块,考虑到后续数据量的快速增长,不是直接写入 MySQL,而是使用了华为云的分布式数据库中间件 DDM。使用了 DDM 之后,可以在业务不感知的情况下,直接增加 MySQL 读实例的个数,线性提升读性能。也支持中间件层面的分库分表,提供海量关系型数据库的操作。简直是为电商系统贴身定制的。

DDM 自身是以集群形式提供服务的,对业务开放的是多个连接 IP 地址。需要有一层负载均衡。如果使用传统的加 LB 的形式做负载均衡,会多一层中转,有性能损耗。所以,直接使用了 MySQL-JDBC 提供的客户端负载均衡能力。

逻辑结构如下图所示:

一个MySQL-JDBC驱动bug引起的血案

业务通过 MySQL-JDBC 的 Loadbalance 能提访问多个 DDM 节点。MySQL-JDBC 提供负载均衡能力。

问题说明

MySQL JDBC 驱动的客户端负载均衡能力,一直运行得好好,性能嗷嗷叫。可是前一阵子竟无故出现业务请求失败。我是负责电商订单模块的,涉及到真实的 Money,这个问题可吓了宝宝一身冷汗……于是赶紧查看了后台日志,发现是访问 DDM 出现了异常,二话不说直接提了工单给华为云 DDM 服务。

一个MySQL-JDBC驱动bug引起的血案

不得不说,华为云的服务还是很好的,不到半个小时就有专门的工作人员联系了我,还跟我一起排查问题。

将我们业务的日志取下来,和 DDM 的支撑人员一起分析,发现报错如下:根本原因竟然是 MySQL 驱动的 bug,导致 StackOverflow 本地栈溢出导致……原来是一个 Bug 引发的血案,误会了 DDM 服务,真是抱歉了

一个MySQL-JDBC驱动bug引起的血案
一个MySQL-JDBC驱动bug引起的血案

从堆栈可以看出来,某个异常,触发了 MySQL-JDBC 的 bug,导致循环调用,直至栈溢出。在华为 DDM 支撑人员的建议下,对驱动代码进行了反编译,从反编译的情况下,可以看到的确是存在循环嵌套的可能。

Loadbalance 轮询连接 –> 同步新老连接的状态 -> 发送 sql 给服务端 -> Loadbalance 轮询连接。

相关代码如下:

一个MySQL-JDBC驱动bug引起的血案一个MySQL-JDBC驱动bug引起的血案一个MySQL-JDBC驱动bug引起的血案

这么明显的 bug,不太相信 MySQL 会没有发现。当前我们使用的是 5.1.44 版本的驱动,查看了下最新的 5.1.66 的代码,发现的确是修复了这个问题的,代码如下:

一个MySQL-JDBC驱动bug引起的血案

通过过滤掉 SET 和 SHOW 语句,避免了循环嵌套的发生。

但是 5.1.66 又引入了新的 bug,由于并不是每个调用 postProcess 的地方都有 SQL,这里的代码会抛空指针异常。MySQL JDBC 的开发者都不做测试的吗……

没办法,分析了下 5.1.44 的代码,发现通过适当的调整 loadBalanceAutoCommitStatementThreshold 这个参数的数值,也可以避免循环嵌套的发生。我们的环境改成了 5,修改之后,平稳运行 1 周,没再出现过问题。

修改方案

loadBalanceAutoCommitStatementThreshold 修改成了 5,但是引入的问题是,如果业务包含一些比较耗时的 SQL,可能会导致 DDM 的负载不均衡。不过,就目前情况来看,DDM 的性能还是比较强劲的~

本文转载自公众号中间件小哥(ID:huawei_kevin)。

原文链接:

https://mp.weixin.qq.com/s/SwQR5OEhnG3AGXXepyTy4g

评论

发布