MySql Too Many Connections

背景:

​ 公司部署的应用是一整套应用服务,在一个Tomcat中包含5个子模块,其中有两个是做数据级联操作,也就是保存各子系统推送的数据,并向它的上级推送。这两个模块对数据库的访问尤其是插入操作比较频繁。另外,这台服务器还部署了MySQL,Redis,Mq等应用

​ 今天早上现场人员说web平台又崩溃了,访问不了,查看应用日志发现大量的MySql Too Many Connections异常,第一反应数据库连接被占满了,导致没有空闲的连接可以使用了。这个时候无论是使用mysql命令直连数据库还是通过Navicat连接数据库都连接不上,show full processlist命令根本使用不了,无法定位是哪个应用出的问题,所以只能先回收被占用的连接,让数据库恢复正常使用。

  1. 使用如下命令,查看当前连接状况

    1
    netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]'

    执行结果如下:

    1
    2
    3
    TIME_WAIT 42906
    ESTABLISHED 13
    LISTEN 1

    发现大量的MySQL连接处于TIME_WAIT状态,只有少数处于连接状态,这显然是不正常的

  2. 可以通过调整内核参数来回收这些处于TIME_WAIT状态的

    1
    2
    3
    4
    5
    6
    7
    vim /etc/sysctl.cnf
    # 添加如下信息
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_fin_timeout = 30
    net.ipv4.tcp_timestamps
    • net.ipv4.tcp_syncookies = 1表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

    • net.ipv4.tcp_tw_reuse = 1表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

    • net.ipv4.tcp_tw_recycle = 1表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

    • net.ipv4.tcp_fin_timeout修改系統默认的TIMEOUT时间

    1、开启tcp_timestamp是开启tcp_tw_recycle,tcp_tw_reuse和tcp_timestamp的前提条件。 2、但是在nat模式下,不用将tcp_tw_recycle和tcp_timestamp同时开启,这会造成tcp超时引发故障。
  3. 使用命令 /sbin/sysctl -p 使刚修改的参数生效

  4. 再次使用命令查看,明显的发现TIME_WAIT的数量减少了,mysql可以重新连上了

  5. 现场恢复使用后,开始排查是什么原因导致的,打开每个模块的配置文件,看到每个模块配置的druid的最大连接数都为200,查看 mysql/conf/my.cnf文件,发现这里配置的 max_connections 为500,max_connect_errors=10000,那么推测,当平台设备上报的采集的人脸、车辆等抓拍记录较多的时候,可能会将整个 druid的连接数全部占用,由于每个模块的最大连接均为200,那么是有可能达到数据的最大连接数500的

  6. 随后修改了每个模块的连接数大小为50,并将先前修改的内核参数还原回去,重启应用,观察情况

  7. 应用到目前为止还处于正常运行状态,需要再进一步观察

延伸:

​ 出现以上问题的原因可能是因为项目中连接池配置不合理导致的,那么应该如何去配置呢

​ 当配置的连接数过大时,可能因为线程间的上下文切换频繁而导致性能的下降,因为大部分的数据库都是将数据存储在磁盘上的,那么在进行数据访问时就必然的要进行磁盘IO操作,在这段时间内,线程是处于“阻塞”状态的,那么此时操作系统可以将这个空闲的CPU服务于其他线程,等IO操作完毕后再切回来。所以,当我们处理的业务是IO密集型的业务时,我们设置的连接数大小可以比核心数大些,这样可以提高吞吐量。

​ 至于,需要设置成多大,还和网络IO,磁盘类型,带宽等有一定的关系,比如,在使用SSD硬盘时,由于读写快,IO阻塞的时间小,可以将连接数设置的更小一些,带宽高的又会比带宽小的阻塞小一些,也可以设置的更小一些。

​ 可以参考的连接数计算公式:(核心数 * 2 ) + 有效磁盘数)

​ 应该尽量去设置一个合适的小连接池和一个等待连接的线程队列,不宜设置过大,像我们最开始设置的200,显然是过大了。