最近我们的Django项目供Java Sofa应用进行tr调用时, 经常会出现一个异常: django.db.utils.OperationalError: (2006, 'MySQL server has gone away'). 本文记录了分析, 本地重现与解决此问题的全过程.

原因分析:

Django在1.6引入长链接(Persistent connections)的概念, 可以在一个HTTP请求中一直用同一个连接对数据库进行读写操作.
但我们的应用对数据库的操作太不频繁了, 两次操作数据库的间隔大于MySQL配置的超时时间(默认为8个小时), 导致下一次操作数据库时的connection过期失效.

Our databases have a 300-second (5-minute) timeout on inactive connections. That means, if you open a connection to the database, and then you don’t do anything with it for 5 minutes, then the server will disconnect, and the next time you try to execute a query, it will fail.

重现问题:

设置mysql wait_timeout为10s

在macOS上的mysql配置文件路径: /usr/local/etc/my.cnf

1
2
3
4
5
6
# Default Homebrew MySQL server config
[mysqld]
# Only allow connections from localhost
bind-address = 127.0.0.1
wait_timeout = 10
interactive_timeout = 10

重启mysql:

1
2
3
4
➜  ~ brew services restart mysql
Stopping `mysql`... (might take a while)
==> Successfully stopped `mysql` (label: homebrew.mxcl.mysql)
==> Successfully started `mysql` (label: homebrew.mxcl.mysql)

检查wait_timeout的值是否已被更新.

1
2
3
4
5
6
7
8
9
mysql> show variables like '%wait_timeout%';
+--------------------------+----------+
| Variable_name | Value |
+--------------------------+----------+
| innodb_lock_wait_timeout | 50 |
| lock_wait_timeout | 31536000 |
| wait_timeout | 10 |
+--------------------------+----------+
3 rows in set (0.00 sec)

重现exception:

1
2
3
4
5
6
7
8
9
10
11
12
>>> XXX.objects.exists()
True
>>> import time
>>> time.sleep(15)
>>> XXX.objects.exists()
True
>>> XXX.objects.exists()
...
django.db.utils.OperationalError: (2013, 'Lost connection to MySQL server during query')
>>> XXX.objects.exists()
...
django.db.utils.OperationalError: (2006, 'MySQL server has gone away')

有意思的一个点是, sleep 10s 之后, 第一次操作数据库, 会出现(2013, 'Lost connection to MySQL server during query’)异常. 之后再操作数据库, 才会抛出(2006, 'MySQL server has gone away’)异常.

解决问题:

第一个最暴力的方法就是增加mysql的wait_timeout让mysql不要太快放弃连接. 感觉不太靠谱, 因为不能杜绝这种Exception的发生.

第二个办法就是手动把connection直接关闭:

1
2
3
4
5
6
7
8
>>> Alarm.objects.exists()
True
>>> from django.db import connection
>>> connection.close()
>>> time.sleep(10)
>>> Alarm.objects.exists()
True
>>>

发现不会出现(2006, 'MySQL server has gone away’)异常了, 但总感觉还是不够优雅.
最终决定在客户端(Django), 设置超时时间(CONN_MAX_AGE: 5)比mysql服务端(wait_timeout = 10)小:

1
2
3
4
5
6
7
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 5,
<other params here>
}
}

但很奇怪没有生效??? 看了源代码, 发现只有在request_started(HTTP request)和request_finished的时候, 在close_if_unusable_or_obsolete才用到CONN_MAX_AGE并去验证时间关闭connection.

具体代码见: python3.6/site-packages/django/db/__init__.py#64

1
2
3
4
5
6
7
8
9
# Register an event to reset transaction state and close connections past
# their lifetime.
def close_old_connections(**kwargs):
for conn in connections.all():
conn.close_if_unusable_or_obsolete() signals.request_started.connect(close_old_connections)
signals.request_finished.connect(close_old_connections)

而我的代码是处理一个任务而不是HTTP请求, 所以不会触发这个signal. 于是我写了一个装饰器, 在任务的开始和结束的时候, 关闭所有数据库连接.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.db import connections

# ref: django.db.close_old_connections
def close_old_connections():
for conn in connections.all():
conn.close_if_unusable_or_obsolete() def handle_db_connections(func):
def func_wrapper(request):
close_old_connections()
result = func(request)
close_old_connections() return result return func_wrapper # ------割-------
@handle_db_connections
def process_trsbrain_request(request):
...

ps. CONN_MAX_AGE默认其实为0, 意味着默认在http请求和结束时会关闭所有数据库连接.

其他:

django.db中connection和connections的区别???

  1. connection对应的是默认数据库的连接, 用代码表示就是connections[DEFAULT_DB_ALIAS]
  2. connections对应的是setting.DATABASES中所有数据库的connection

Django (2006, 'MySQL server has gone away') 本地重现与解决的更多相关文章

  1. [django1.6]跑批任务错误(2006, 'MySQL server has gone away')

    有个django的定时任务的需求,调用django的orm来对数据库进行数据处理.  在交互环境下直接启动pyhton脚本没有问题,放在定时任务中时候,总是出现 (2006, 'MySQL serve ...

  2. flask+mako+peewee(下)(解决了Error 2006: MySQL server has gone away)

    这篇主要介绍在这次项目中使用的peewee 文档地址:http://peewee.readthedocs.org/en/latest/index.html 首先我们要初始化一个数据库连接对象.这里我使 ...

  3. MySQL(Navicat)运行.sql文件时报错:[Err] 2006 - MySQL server has gone away 的解决方法

    背景: 今天导入一个数据量很大的.sql文件时,报错: 原因: 可能是sql语句过长,超过mysql通信缓存区最大长度. 解决:1. 编辑 MySQL 安装目录下的 my.ini,在最后添加以下内容: ...

  4. MySQL导入sql脚本错误:2006 - MySQL server has gone away

    到如一些小脚本很少报错,但最近导入一个10+M的SQL脚本,却重复报错: Error occured at:2014-03-24 11:42:24 Line no.:85 Error Code: 20 ...

  5. 【mysql】之MySQL导入sql脚本错误:2006 - MySQL server has gone away

    到如一些小脚本很少报错,但最近导入一个10+M的SQL脚本,却重复报错: Error occured at:2014-03-24 11:42:24Line no.:85Error Code: 2006 ...

  6. #2006 - MySQL server has gone away 问题解决方法 (全) (转)

    #2006 - MySQL server has gone away 问题解决方法 原文地址:http://www.cnblogs.com/bisonjob/archive/2009/08/18/15 ...

  7. MYSQL导入数据报错|MYSQL导入超大文件报错|MYSQL导入大数据库报错:2006 - MySQL server has gone away

    导SQL数据库结构+数据时,如果数据是批量插入的话会报错:2006 - MySQL server has gone away. 解决办法:找到你的mysql目录下的my.ini配置文件(如果安装目录没 ...

  8. Yii2 解决2006 MySQL server has gone away问题

    Yii2 解决2006 MySQL server has gone away问题 Yii2版本 2.0.15.1 php后台任务经常包含多段sql,如果php脚本执行时间较长,或者sql执行时间较长, ...

  9. SQLyog恢复数据库报错解决方法【Error Code: 2006 - MySQL server has gone away】

    https://blog.csdn.net/niqinwen/article/details/8693044 导入数据库的时候 SQLyog 报错了 Error Code: 2006 – MySQL ...

随机推荐

  1. maven编译maven-surefire-plugin插件报错

    1.编译错误信息: [INFO] ------------------------------------------------------------------------ [ERROR] Fa ...

  2. [C++设计模式] proxy 代理模式

    代理模式:为其它对象提供一种代理以控制对这个对象的訪问. Proxy: 保存一个引用使得代理能够訪问实体.若RealSubject和Subject的接口同样,Proxy会引用Subject,就相当于在 ...

  3. 猫猫学iOS之小知识之_xcode插件的删除方法_自己主动提示图片插件KSImageNamed有时不灵_分类或宏之类不能自己主动提示,

    猫猫分享,必须精品 原创文章.欢迎转载. 转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243 一:解决解决自己主动提示图片插件KSImageNamed有时不 ...

  4. nyoj43 24 Point game(DFS)

    题目43 题目信息 pid=43" style="text-decoration:none; color:rgb(55,119,188)">执行结果 本题排行 讨论 ...

  5. CrtmpServer 接收推送视频流 注册流基本流程

    今天研究了CrtmpServer 将客户端推动过来的视频流注册到服务的流程,记录下来,以备后用. 图1 注册前端视频流流程

  6. 基于bootstrap+MySQL搭建动态网站

    这个只是在上个练习项目中的后台管理项目加入了MySQL,数据不是写死的,而是从数据库中获取到的,获取到数据执行增删改查操作,没什么 计数难度,不做介绍

  7. asp识别手机端

    <script type="text/javascript"> var mobileAgent = new Array("iphone", &quo ...

  8. 多项式相乘快速算法原理及相应C代码实现---用到fft

    最近认真研究了一下算法导论里面的多项式乘法的快速计算问题,主要是用到了FFT,自己也实现了一下,总结如下. 1.多项式乘法 两个多项式相乘即为多项式乘法,例如:3*x^7+4*x^5+1*x^2+5与 ...

  9. 《TCP/IP具体解释卷2:实现》笔记--UDP:用户数据报协议

    用户数据报协议.即UDP,是一个面向数据报的简单运输层协议:进程的每次输出操作仅仅产生一个UDP数据报,从而发送 一个IP数据报. 进程通过创建一个Internet域内的SOCK_DGRAM类型的插口 ...

  10. 九度OJ 1118:数制转换 (进制转换)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3873 解决:1494 题目描述: 求任意两个不同进制非负整数的转换(2进制-16进制),所给整数在long所能表达的范围之内.     不 ...