解决ProxySQL连接MySQL产生大量TIME_WAIT连接的问题
有时候解决一个问题很简单,但是其中发现问题和深入问题的过程却值得我们反复思考
出现了问题
最近在测试环境新搭的一套proxysql
忽然无法正常登录了,一直提示连接hostgroup
超时。
我首先跳过proxysql
,直接连接后端的mysql
节点,确认是proxysql
的问题还是mysql
的问题。
但是连接一直处于进行中的状态,不提示登录成功也不提示登录失败。
初步判断和尝试解决
初步判断应该是mysql
的连接数被打满了。
使用命令查看连接数
1 | netstat -naplt|grep 6033|wc -l |
连接数显示mysql
的连接已经被占满了。
由于后端的mysql
节点只通过proxysql
来访问,其他的程序并不知道mysql
实例的端口号,所以尝试重启proxysql
来释放连接。
再次尝试连接后端mysql
节点,这次直接提示连接数过多,连接失败。
显然,重启proxysql
并没有成功的解决问题。
挖掘问题并再次尝试解决
仔细查看netstat
输出的信息。
发现绝大部分的连接都是TIME_WAIT
。
之后,通过重启mysql
暂时的清理掉了这些连接,但是,首先,重启mysql
的成本过高,在线上根本不可行,其次,暂时清理掉TIME_WAIT
的连接之后,TIME_WAIT
的连接数又很快的涨了上来。这并没有从根本上解决问题。
尝试通过proxysql
的参数进行连接数限制,但是,TIME_WAIT
状态的连接根本不被计算在proxysql
的连接中,无法被限制。
查阅资料暂时解决了问题
查询了相关资料后,发现可以通过修改Linux内核参数
来优化TCP连接
。
编辑/etc/sysctl.conf
1 | 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; |
通过开启tcp复用
,tcp快速回收
,修改tcp fin超时时间
依然无法降低TIME_WAIT
连接的数量。
最后,通过修改TIME_WAIT
的最大保持数量,将TIME_WAIT
的连接数量控制在mysql
的最大连接数以内,暂时保证了mysql
的可用性。
但是,我们依然并没有从根本解决问题。
深入了解TIME_WAIT
连接尝试解决问题
为了解决问题,我们先了解一下TIME_WAIT
是什么。
在关闭TCP
连接的四次握手中,客户端
先向服务器端
发送FIN
报文,告诉服务器端“我要断开连接了”,服务器端
收到FIN
后会回复一个ACK
,表示收到断开连接的请求,但此时服务器端
可能仍有数据未发送完,当服务器将数据发送完成后,服务器端
会发送一个FIN
报文,表示可以断开连接,客户端
接收到FIN
报文以后会发送一个ACK
报文,此时客户端
会发送一个ACK
报文,然后客户端
进入TIME_WAIT
状态,当等待2MSL(两个最大报文段生存时间)
之后,如果没有再接收到服务器端
的请求,连接就会自动断开。
换句话说,当连接进入TIME_WAIT
状态以后,我们不需要做任何事情,也无法做任何事情,我们所能做的唯一的事情就是等待一段时间以后,TIME_WAIT
的连接就会自动断开。
所以TIME_WAIT
的问题并不是连接没有被释放,而是这些TIME_WAIT
的连接被创建的太多了。
由于在该环境中,mysql
只有proxysql
在连接,所以我尝试修改了一些proxysql
中关于连接的参数mysql-free_connections_pct
、mysql-max_stmts_per_connection
等,但是依然无效。
问题解决和总结
最后,该问题的解决是通过修复mysql
中monitor
用户而解决的。
monitor
用户是proxysql
用以监控mysql
的用户,proxysql
会定时调用该用户从mysql
中获取数据。而在mysql
中该用户其实并没有被正确的创建,虽然在一开始我就从log
中发现了这个问题,但是我并不认为这会导致mysql
节点不可用,所以就忽略了这问题。但是,没有想到,虽然monitor
用户无法正常的连接到mysql
,但是会创建一个TIME_WAIT
的连接。而且,由于不同的尝试连接,导致连接数过大,造成mysql
无法使用。
在解决了问题之后,又通过简单的python
程序验证了这一状况。
1 | import mysql.connector |
在上面的脚本中,使用错误的用户名密码不断地连接数据库。
然后,通过监控TIME_WAIT
数量,发现虽然连接都失败了,但是每一次尝试连接都会产生一个TIME_WAIT
的连接。