MySQL server has gone away

在查看celery.log时发现(2006, 'MySQL server has gone away')

Posted by dzt on January 2, 2019

前言

运行了很长时间的celery之后,发现有任务一直没有完成,也没有失败,一直处于执行当中,就去查看了celery的log。

截取了少部分error:

  File "/home/ubuntu/www/cmxcelery/kkwork/local/lib/python2.7/site-packages/MySQLdb/cursors.py", line 205, in execute
    self.errorhandler(self, exc, value)
  File "/home/ubuntu/www/cmxcelery/kkwork/local/lib/python2.7/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue
OperationalError: (2006, 'MySQL server has gone away')

怀疑是执行任务时间过长(一个任务大概会花4、5个小时),mysql连接时间过长,但并没有操作mysql(只会在开始和结束的时候进行mysql操作),导致client和MySQL server之间的链接断开了。

原因一、MySQL 服务宕了

判断是否属于这个原因的方法很简单,进入mysql控制台,查看mysql的运行时长

mysql> show global status like 'uptime'
    -> ;
+---------------+--------+
| Variable_name | Value  |
+---------------+--------+
| Uptime        | 428464 |
+---------------+--------+
1 row in set (0.01 sec)

1 row in set或者查看MySQL的报错日志,看看有没有重启的信息

如果uptime数值很大,表明mysql服务运行了很久了。说明最近服务没有重启过。 如果日志没有相关信息,也表名mysql服务最近没有重启过,可以继续检查下面几项内容。

原因二、 mysql连接超时

即某个mysql长连接很久没有新的请求发起,达到了server端的timeout,被server强行关闭。 此后再通过这个connection发起查询的时候,就会报错server has gone away

mysql> show global variables like '%timeout';
+----------------------------+----------+
| Variable_name              | Value    |
+----------------------------+----------+
| connect_timeout            | 10       |
| delayed_insert_timeout     | 300      |
| innodb_lock_wait_timeout   | 50       |
| innodb_rollback_on_timeout | OFF      |
| interactive_timeout        | 28800    |
| lock_wait_timeout          | 31536000 |
| net_read_timeout           | 30       |
| net_write_timeout          | 60       |
| slave_net_timeout          | 3600     |
| wait_timeout               | 28800    |
+----------------------------+----------+
10 rows in set (0.01 sec)

wait_timeout 是28800秒,即mysql链接在无操作28800秒后被自动关闭

所以等待时间小于任务执行的时间,mysql被自动关闭了

解决方法:

1、立即生效

mysql> set global wait_timeout=60*60*24;

该命令是将wait_timeout设置为24小时。 用这种方法,修改完立即生效. 如果重启mysql,又恢复原来的28800秒。

2、永久生效

该方法修改完之后, 需要重启mysql才能生效. 编辑mysql配置文件my.cnf,添加或修改为下面这条命令. (我的mysql配置文件路径: /etc/mysql/my.cnf )

[mysqld]
wait_timeout = 86400
interactive_timeout = 86400

当时在 /etc/mysql/my.cnf中修改的时候 没有加上 [mysqld]这个标志 导致语法错误 而不能启动mysql

还有网上别人遇到的错误是 修改了变量之后没有生效。

这个方面的原因大概就是:

my.cnf中 还有引用了两个地方的设置:

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

这两个文件里面的mysql.conf配置也会被引进来 ,导致my.cnf中的配置被这两个文件中的配置覆盖。

原因三、 mysql请求链接进程被主动kill

这种情况和原因二相似,只是一个是人为一个是MYSQL自己的动作

mysql> show global status like 'com_kill';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_kill      | 1     |
+---------------+-------+
1 row in set (0.05 sec)

原因四、 Your SQL statement was too large.

当查询的结果集超过 max_allowed_packet 也会出现这样的报错。定位方法是打出相关报错的语句。

用select * into outfile 的方式导出到文件,查看文件大小是否超过 max_allowed_packet ,如果超过则需要调整参数,或者优化语句。

mysql> show global variables like 'max_allowed_packet';
+--------------------+----------+
| Variable_name      | Value    |
+--------------------+----------+
| max_allowed_packet | 16777216 |
+--------------------+----------+
1 row in set (0.00 sec)

修改参数:

mysql> set global max_allowed_packet=1024*1024*16;
mysql> show global variables like 'max_allowed_packet';

最后的解决办法:

主动断开连接 再进行连接数据库操作

from django.db import connection
from time import sleep


connection.close()
sleep(10)

2019-01-04 22:17