//线上一个client连接失败报错//
今天下午,线上的MySQL数据库出现了一个连接中断的报错,大概的报错内容如下:
代码语言:javascript复制200701 17:57:50 [Warning] Aborted connection 50367424 to db: 'xxxxx' user: 'srv_bigdata_rwh' host: 'xxxxx' (Got an error reading communication packets)
200701 17:57:50 [Warning] Aborted connection 50367424 to db: 'xxxxx' user: 'srv_bigdata_rwh' host: 'xxxxx' (Got an error reading communication packets)
200701 17:57:50 [Warning] Aborted connection 50367424 to db: 'xxxxx' user: 'srv_bigdata_rwh' host: 'xxxxx' (Got an error reading communication packets)
200701 17:57:50 [Warning] Aborted connection 50367424 to db: 'xxxxx' user: 'srv_bigdata_rwh' host: 'xxxxx' (Got an error reading communication packets)
200701 17:57:50 [Warning] Aborted connection 50367424 to db: 'xxxxx' user: 'srv_bigdata_rwh' host: 'xxxxx' (Got an error reading communication packets)
200701 17:57:50 [Warning] Aborted connection 50367424 to db: 'xxxxx' user: 'srv_bigdata_rwh' host: 'xxxxx' (Got an error reading communication packets)
200701 17:57:50 [Warning] Aborted connection 50367424 to db: 'xxxxx' user: 'srv_bigdata_rwh' host: 'xxxxx' (Got an error reading communication packets)
看样子是读取通信包的时候出现的错误。于是我查询了一下MySQL的官方文档,看看这个到底是啥错。
首先来看通信包在MySQL中的定义:
A communication packet is a single SQL statement sent to the MySQL server, a single row that is sent to the client, or a binary log event sent from a master replication server to a slave.
可以理解为是client发送给server的一条SQL、也可以是server发送给client的一条记录,或者是master发送给slave的一个binlog事件。
通信包的大小由max_allowed_packet控制,最大可以设置成1GB,client的默认值是16MB,server的默认值是4MB。
对于通讯报出现错误的情况,MySQL官方文档给出了以下分析方法:
1、分析错误日志或者通用日志
2、分析状态变量 Aborted_xxx 和
Connection_errors_xxx
3、分析performance schema中的host_cache表
如果客户端一开始连接的时候,就发生此类报错,那么报错可能的原因有:
1、客户端尝试连接服务器,但是却没有权限
2、客户端使用了不正确的密码
3、连接的通讯包格式不正确
4、连接时长超过了connect_timeout秒,但是依旧没有获取到通讯包
如果客户端一开始连接没有问题,但是后续连接中断,并报错,伴随abort_clients和connection_erros_xxx变量增长,则有可能是下面的原因:
1、客户端退出后没有调用mysql_close函数
2、客户端已经等待超过wait_timeout或者interactive_timeout秒没有和server交互,MySQL主动回收了连接线程。
3、数据传输过程中,客户端发生了断开的情况
4、max_allowed_packet值设置太小,或者查询需要更多的内存空间
MySQL Client和Server交互原理:
有了上面的这些建议,接下来就是尝试解决问题了。在此之前,我们还应该了解下client和server端利用tcp/ip协议建议连接,然后进行数据交互的原理了:
1、MySQL server端根据client端传来的SQL,获取数据记录,写到net_buffer中,其中net_buffer受参数net_buffer_length控制,默认是16KB
代码语言:javascript复制mysql> show variables like "%net_buffer%";
------------------- -------
| Variable_name | Value |
------------------- -------
| net_buffer_length | 16384 |
------------------- -------
1 row in set, 1 warning (0.00 sec)
2、重复获取记录,直到net_buffer写满,然后调用网络接口发送数据,写入linux的本地网络栈
3、net_buffer发送成功,清空net_buffer,接着取剩余的满足条件的数据记录,并写入net_buffer
4、如果Linux的本地网络栈写满了,则net_buffer的发送过程需要等待。
本地网络栈的位置,一般保存在下面的文件中:
代码语言:javascript复制[root@VM_48_10_centos ~]# cat /proc/sys/net/core/wmem_default
212992
如果Linux本地网络栈被写满,则net_buffer的传输会进入等待状态,此时,在show processlist的state状态中,会出现"sending to client"字样,如果你的系统中经常出现这种字眼,很明显,返回的数据太多了,此时就要评估业务的返回结果是否合理了。适当的调大net_buffer_length的值可以解决本地网络栈被写满的状态。
开始尝试解决问题:
首先我尝试性的修改了server端和client端的max_allowed_packet的参数,原本设置的时间是32MB,我将它改成了64MB,重新查看错误日志,似乎问题没有解决,错误还一直存在。
调整max_allowed_packet的值没有用处,再来调整net_buffer_length的值试试,该值默认是16k,最大值为1MB,将它调大到32k,发现问题已经解决了。
问题虽然通过上面的尝试解决了,还有一点没有想明白,就是在官方文档中有看到说:"每个客户端线程都有一个连接缓冲区和一个结果缓冲区,这两者都以net_buffer_length给定的大小作为初始大小,但可以根据需要动态扩展到max_allowed_packet字节大小。执行完SQL语句后,结果缓冲区会自动缩小为net_buffer_length",这不就意味着max_allow_packet这个参数才是决定通讯包的最终大小的,为什么在我的案例中,第一次调整了max_allowed_packet之后,没有作用,报错依旧,难道是因为响应有滞后?
这个问题后面还得重新排查下。就目前来看,调整max_allowed_packet和net_buffer_length是可以解决此类报错的。