PHP中MySQL server has gone away问题

一.背景

  之前在Codeigniter里面写过类似console命令行的脚本. 脚本里存在sleep语句时间比较久, 导致出现一个现象就是sleep之前的SQL都是操作成功的,但是sleep之后,再执行SQL操作竟然报错: MySQL server has gone away. 也就是mysql的这个连接失效. 后来分析才知道, MySQL中存在2个重要的配置参数:

  1. interactive_timeout
  2. wait_timeout

  这2个参数的单位都是秒(s). 默认是8小时(28800). interactive_timeout从单词上看指的是交互超时时间. mysql的连接方式一般分为2种, 一种称之为”交互式”, 一种称为”非交互式”. 一般常见的使用mysql -u root xxx之类的或者主从复制的连接为”交互式连接”, 使用如Java的JDBC、PHP的PDO驱动连接的方式一般是”非交互式连接”. 然而interactive_timeout如果未修改的情况下,这个值是一直不会变的,但是wait_timeout在不同连接方式下,值是不一样的.

  wait_timeout在”交互式连接”下, 其值是interactive_timeout的值. 如果在”非交互式连接”情况下, 则wait_timeout的值是原来mysql.cnf中配置的原始值.

  最终起作用的只是wait_timeout的值.这配置项限定了处于sleep状态(通过 show processlist查看当前连接数情况)的连接,如果这个连接sleep休眠时间超过wait_timeout的值,则这个连接被断掉或者说被清理掉.

二.wait_timeout分析

1.首先查看mysql.conf配置

在这里插入图片描述
首先我们配置了interactive_timeout=10 wait_timeout=5, 此时通过mysql客户端(交互式连接)查看这2个配置项的值: show variables like ‘%timeout%’;

2.交互式连接

在这里插入图片描述
客户端的结果: wait_timeout竟然不是我们msyql.conf配置的10s, 而是 5s.

那我们再来看看PHP连接MySQL(非交互式连接),执行相同的语句,得到什么结构:

3.非交互式连接

在这里插入图片描述

在这里插入图片描述
此时wait_timeout是我们原来在mysql.cnf配置的值了.

综上所述: wait_timeout这个值,在不同的”连接模式”下面,拿到的值是不一样的.

三.gone away原因分析

   结合上面的情况,我们就知道了。 一开始某些SQL执行成功,但是后面的SQL执行失败报错gone away,大部分原因就是这个连接被闲置超过了wait_timeout,mysql服务器单方面断掉了这个连接。但是客户端代码,还是在用这个连接变量,以为连接还是ok的(其实mysql server端已经断开了,只是我们以为这个连接还有效),去执行SQL必然报错.

  那么我们怎么解决这个情况呢?

1.可以适当调整wait_timeout的值, 调大一点,这样不容易触发这个gone away情况.但是弊端就是,sleep的长连接不被清理,资源白白浪费了.

2.通过try-cach如果抛出gone way msyql的连接问题, 先把之前的db调用close().在重新获取db连接open,然后再执行之前的代码. 不过代码看起来感觉很蛋疼.伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
$db = db();

try {
fun1$db); // 一开始执行成功
sleep(3600*10) // 假设sleep了10个小时
fun2($db); // 10小时之后 由于连接已经被mysql干掉 导致报错 gone away
}catch(Exception $e) {
// 报错后 我们把无效的连接close 在 open新的连接
$db->close();
$db->open();
// 再拿到新的连接执行
// fun2($db)
}

3.如果是使用类似swoole或者easyswoole框架, 建议使用mysql pool连接池的形式.并且一般连接池都有关于心跳检查ping、连接存活检测间隔时间设置、最大闲置连接数等等设置, 只要配置一次就好了。 例如可以配置测活连接间隔时间短一点,来保证连接不会被msyql服务器干掉.例如 easyswoole配置:

在这里插入图片描述
例如之前我设置wait_timeout=10, 但是如果我没修改这个easyswoole的mysql连接池测活间隔时间变小, 同样会出现gone way的情况. 第一次访问接口成功返回SQL执行结果,但是超过10s以后我再次访问接口,报错mysql has gone away。修改setIntervalCheckTime()之后,就不会出现这个问题了. 我们通过mysql的show processlist;查看连接数情况:

在这里插入图片描述
这些都是easyswoole帮我们维护的连接数. 当sleep超过3秒时, 由于检查时间是3秒存活, 连接池帮我们保活检查, sleep的时间又从0开始计算.

CSDN原文: 原文地址