最近我们新加了一台机,在xx的机器上访问MySQL,可以正常连上,执行show databases
等马上有结果的指令,可以正常返回,但如果表记录又上千万,select count(1) from xxx
的时候,则会没有返回。
犹记得几个月前,我们在C机有个脚本通过http方式提交数据到远程机器S,S将结果存到MySQL,同样也是经过XFW,由于处理时间比较长(大概1分钟吧),C这边一直卡住, 因为没有收到S的返回,但在S通过tcpdump抓包发现,S是有返回的,而且尝试多次返回,说明什么?连接断了呗,但C和S都感知不到这个断开的操作,感觉是XFW检测到此数据连接没有数据发送,把它断了。
最后是在S上加了索引,使得存入MySQL操作秒返回,才解决了这个问题。天下武功,唯快不破,和XFW斗亦如是。
首先说明下本次新加的机器,我们升级MySQL到5.7,以往的机器都是5.5,那是不是5.7的问题呢?
以下都是在C(SUSE系统)上连接:
1.连MySQL 5.7(A)时,实际早就挂了(服务器端top看MySQL的CPU占用),但客户端这边还是阻塞状态,过了很久才显示:
ERROR 2013 (HY000): Lost connection to MySQL server during query
2.连MySQL 5.5时(B),偶尔会卡住,在服务器端抓包,没有数据过来,意味着实际上还没有连到过去,但如果能连过去,一般可以获取到返回
但我们后来通过tcpdump抓包发现,第一点的结论其实是错的,因为在MySQL的CPU占用降下去之后,从3306端口是有数据发出去的,也就是并没有挂,查询有结果,并有发出去,但客户端没有收到,C端等到超时了才退出并报错
刚好有1台CentOS系统的机器,测试下,结果和上面SUSE系统一样
基于上面一开始提到的http提交数据的问题, 我们又感觉是tcp的问题,怎么验证?我们在A上开了ssh的密码登录,然后在C上ssh到A,然后A上执行mysql连接,再进行查询,结果发现,也是没有返回,最后提示
Disconnected; connection lost (Connection closed.).
那很明显了,就是tcp连接断开了
上网查询发现CentOS上,ssh有一个维持心跳的参数,于是试了下
# 5s发送一次心跳包
ssh -o ServerAliveInterval=5 -pxxx username@A
通过上面的参数连接后,发现此时可以正常获取结果了
记得关闭A上的ssh密码登录
通过以上测试,发现问题出现在tcp,那就有解决的方向了,但MySQL是没有这种维持心跳的参数的,但我们找到了一个libkeepalive.so的软件,通过它封装下,可以设置应用层的心跳,那就不用设置内核参数了
LD_PRELOAD=libkeepalive.so KEEPIDLE=5 KEEPCNT=3 KEEPINTVL=5 mysql -h xxx
但比较遗憾, 没有生效,无论是SUSE还是CentOS
我们在C端把内核参数设置下,感觉是比较重(需要修改内核参数)的解决办法。
# 多久探测一次,此处为5s
sysctl -w net.ipv4.tcp_keepalive_intvl=5
# 空闲多久后开始探测,此处为5s
sysctl -w net.ipv4.tcp_keepalive_time=5
通过上述设置,再直接用mysql连接就正常了。
以下是系统默认值,net.ipv4.tcp_keepalive_probes
的意思是,每「net.ipv4.tcp_keepalive_time 」
间隔,就探测一次,探测「net.ipv4.tcp_keepalive_probes」
次,此处为9次,就停止探测
net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 5
为避免影响其他程序,建议只在测试时通过命令行设置,在测试完后,通过sysctl -p还原为系统默认配置