记录一次MySQL数据抽取问题的解决

1,568 阅读3分钟

1.问题现象

某天,突然收到少量数据抽取任务失败的报警,均类似如下报错: Communications link failure The last packet successfully received from the server was xxx milliseconds ago.

起初认为是数据库服务不稳定,但任务自动重试多次后,仍然报错。且与DBA沟通后,确定数据库所属实例运行正常。此时还未能确定报错原因。

由于抽取任务已稳定运行有一段时间了,因此询问DBA报错的相关库表,近期是否做过任何调整(被坑出经验了,属于是)。

image.png

果然,问题就在这里:wait_timeout被调整为100 秒

2.业务背景

由于该业务的特殊场景,导致3台MySQL实例上共存在1W+分库,后端的同事为了避免服务端维持的连接数过多导致的查询报错,要求DBA将相关实例上的 wait_timeout 调整到 100s,也即一次连接在100s内如果没有活动,服务端将会自动kill该连接。

3.对于抽取的影响

“进行MySQL数据同步时,DataX是通过JDBC 相关接口发送数据读取SELECT SQL语句,进而读取MySQL数据库表数据的。如果MySQL表过大、或者有不合适的数据抽取控制where条件、或者数据表缺少相关的索引机制,导致数据读取SQL长时间不返回,有可能到达wait_timeout时间限制值,导致连接被MySQL服务端关闭,数据同步任务失败。

此时需要增大wait_timeout大小或者尝试优化数据读取SQL(表索引、有索引列的where条件、减少不必要的函数列等)的性能。”

引用自:MySQL超时参数以及相关数据集成、DataX数据同步案例分享

4.问题的解决

既然已经有了罪魁祸首,那就需要思考该如何解决了。

最开始讨论过后端采用数据库连接池避免出现过多的短连接,但即使每个库仅维持2个长连接,3台实例上2W+的连接数也是无法实现的。

既然后端和数据库端无法解决,那只能在数据抽取端思考解决方案了。

既然抽取失败是抽取的连接超过 wait_timeout ,那就存在两个角度去解决:

  1. 避免数据抽取的连接超过 wait_timeout 的设定值;
  2. 将抽取任务的连接的 wait_timeout 设定值调大。

方案一在我考虑了三秒钟后就被pass了:造成连接超时的原因难以定位,且不易优化。(可能是抽取工具的连接维活机制问题,也可能有其他因素影响)

最开始想到的是MySQL是否支持基于用户的环境参数,基于不同用户,Server端采用不同的配置参数。 但被DBA告知,MySQL不存在相应的功能。

经过一番面向谷歌开发,发现了会话参数这个思路。 Setting the MySql wait_timeout session variable

在抽取配置参数时,增加了会话初始命令,测试一番后,问题顺利解决,既不影响后端大量的短连接自动关闭逻辑,也不会造成数据抽取任务超时失败。

image.png

datax:{"session": ["SET @@local.wait_timeout=xxx"]} datax MySQL会话参数如何定义

spark:{"sessionInitStatement": "SET @@local.wait_timeout=xxx;"} JDBC To Other Databases

5. 总结

虽然本次问题的解决方案并非最完美的(比如超时参数为经验值,可能随着数据量上涨而无法满足需求等等)。后续可能会采用cdc + hudi 的方案,避免每次全量抽取对数据库造成的压力。

但可以用最小的代价,快速解决当前的报错问题。并且满足业务端以及我们数据抽取的需求,对两方造成的干扰较低。

如果有任何想法,欢迎评论区交流哈。