我最近从Django 1.4升级到Django 1.7,因为我不断收到一些脚本的错误消息,有时候会出现:
OperationalError: (2006, 'MySQL server has gone away')
脚本很长或连续运行的任务可能涉及几分钟内不与数据库通信的阶段,因此连接超时。但是,在我升级之前,这没有问题,因为Django似乎会自动重新建立连接。现在它并不意味着任务经常在中间停止和失败。
有谁知道发生了什么变化以及如何解决它?
它可能与该票证/修复有关:https://code.djangoproject.com/ticket/21463
非常感谢!
答案 0 :(得分:26)
这种行为的原因是持久连接数据库,这是在Django 1.6中引入的。
要防止连接超时错误,您应该将CONN_MAX_AGE
中的settings.py
设置为在MySQL配置(wait_timeout
)中小于my.cnf
的值。在这种情况下,Django检测到连接需要在MySQL抛出之前重新打开。 MySQL 5.7的默认值是28800秒。
settings.py
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'CONN_MAX_AGE': 3600,
<other params here>
}
}
文档:https://docs.djangoproject.com/en/1.7/ref/settings/#conn-max-age
my.cnf
:
wait_timeout = 28800
文档:https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout
答案 1 :(得分:15)
我有一个正在运行的后台进程rqworker
,它执行单独的作业以在用户操作后刷新一些数据。
我总是OperationalError: (2006, 'MySQL server has gone away')
如果在wait_timeout
秒以上没有用户操作。
即使我将CONN_MAX_AGE设置为小于MySQL wait_timeout
。
据我所知,如果Django通过此超时自动检查并关闭其连接,则更改CONN_MAX_AGE可能会有所帮助。但是Django 1.7.x仅在每个请求之前和之后检查它(参见django/db/init.py#L101-L112)。
如Django ticket 15119中所述,我们可以看到Django在执行每个查询之前正在进行ping以验证连接是否处于活动状态。此行为已在commit 282b2f4中修复。
Django开发人员在https://code.djangoproject.com/ticket/21597#comment:29
中为所有问题提供了一个简短的答案因此,我的rqworker
进程必须为每个新作业验证连接本身。 (注意:如果我们关闭连接,那么Django将创建一个新连接。)
我将按照每个请求的方式使用Django作业,并在每个作业之前和之后调用django.db.close_old_connections()
。是的,CONN_MAX_AGE
应该小于MySQL wait_timeout
,因为Django不检查MySQL server has gone away
方法中是否django.db.close_old_connections()
。
答案 2 :(得分:8)
在Django 1.9上:
我有一个正在进行的Django Shell,它在unix screen上无人值守,运行了48多个小时。
当我返回并运行<some_model>.objects.filter
时,它就扔了
OperationalError: (2006, 'MySQL server has gone away')
快速import django.db; django.db.close_old_connections()
帮了我大忙。
我在Django Docs for 1.9上找不到close_old_connections()
的文档,但这是direct link to its implementation in Django Codebase on Github
答案 3 :(得分:5)
在django 1.6中,当wait_timeout(mysql)通过时,然后数据库访问导致(2006,&#39; MySQL服务器已经消失&#39;)错误。 这不是django 1.5.1中的情况。
当使用运行django代码的工作人员(使用gearman)时,我注意到了这个错误。
重现:
通过编辑/etc/mysql/my.cnf将超时设置为低值 在[mysqld]
下添加以下内容wait_timeout = 10
interactive_timeout = 10
然后
%python manage.py shell
>>> # access DB
>>> import django.contrib.auth.models
>>> print list(django.contrib.auth.models.User.objects.all())
>>> import time
>>> time.sleep(15)
>>> print list(django.contrib.auth.models.User.objects.all())
现在你收到了错误。
我在网上找到的简单解决方案是在访问之前调用django.db.close_connection()
>>> import django.db
>>> django.db.close_connection()
>>> print list(django.contrib.auth.models.User.objects.all())
工作正常。
答案 4 :(得分:3)
我们也注意到了这一点。将CONN_MAX_AGE设置为小于MySQL / MariaDB的wait_timeout的上述答案适用于网络。
对于长时间运行的任务,这似乎不起作用。相反,每当我们执行一项长期运行的任务时,我们就会将其包装并关闭连接。
我们将此与我们自己的自定义池结合使用。接受或离开它 - 默认的Django没有控制 - 不是我们喜欢的生产。我们设置一个最大池来杀死服务器,然后用太多连接杀死数据库。将它用作任务的装饰器:
@close_db_connection() def task_do_something(): 打印&#39;您好
'''
Created on Dec 23, 2017
@author: Kevin
'''
from functools import wraps
def close_db_connection(ExceptionToCheck=Exception, raise_exception=False, notify=False):
"""Close the database connection when we're finished, django will have to get a new one..."""
def deco_wrap(f):
@wraps(f)
def f_wrap(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
raise e
finally:
from django.db import connection;
connection.close();
return f_wrap
return deco_wrap
答案 5 :(得分:2)
对于某些人来说,超时可能是一个问题,但在尝试编写一个非常大的BLOB字段时遇到了这个问题。我通过增加mysql配置文件中允许的最大数据包大小来解决它...
max_allowed_packet的4M =
更改为/etc/my.cnf后别忘了重启mysql。这页帮助......
http://dev.mysql.com/doc/refman/5.5/en/packet-too-large.html
答案 6 :(得分:2)
set.seed(24)
df <- tibble(X1 = rnorm(10), X2= sample(c(1, 0, NA), 10, replace = TRUE), X3 = letters[1:10], X4 = sample(20, 10, replace = TRUE), X5 = sample(c(1, 0), 10, replace = TRUE))
settings.py :
pip install mysql_server_has_gone_away
在我的情况下,我使用的是长期请求(Webocket)的django ORM。 DATABASES = {
'default': {
'ENGINE': 'mysql_server_has_gone_away'
}
}
在我的情况下不起作用。
在一开始,我创建了一个包装器,尝试捕获错误,但是由于django的延迟加载,尚不清楚您应该为它包装什么东西。因此,我最终在整个项目中复制了这段代码,这是种麻烦。而不是写例如CONN_MAX_AGE
我会做User.objects.get(id=3)
,数据库是do_db(User.objects.get, id=3)
。
当深入django后端时,我们可以在连接级别上迁移此解决方案。因此,每个进入db的查询都将被包裹起来:
settings.py :
try: return callback(*args, **kwargs); catch e: conn.close(); callback(*args, **kwargs)
大声笑/base.py:
DATABASES = {
'default': {
'ENGINE': 'lol'
}
}
您应该注意,如果在原子操作期间mysql断开连接,将会有事务问题。但是不幸的是,没有别的办法了。
答案 7 :(得分:1)
我面临着同样的问题,我的解决方案在哪里。我是Django的新手,现在已经很晚了,但是我正在发布解决方案。也许会帮助某人。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'your_dabase',
'USER': 'your_user',
'PASSWORD': 'your_password',
'HOST': 'your_host',
'PORT': 'your_port',
'CONN_MAX_AGE': 290,
},
'OPTIONS': {
'timeout':20,
}
}
我添加了 CONN_MAX_AGE 和 OPTIONS ,它现在运行良好。
答案 8 :(得分:0)
我知道这是一个老问题,但就我而言,解决方案不在当前的任何答案中。
我的问题的根源在于服务器端。通过检查服务器超时设置(使用 show global variables like '%timeout'
),我发现 wait_timeout
变量设置为 120 秒,因此任何需要这么长时间的任务都会在我试图检索或保存数据。添加 CONN_MAX_AGE
没有任何区别。
解决方案 #1 - 更改服务器的 wait_timeout 设置(如果您有权限这样做)
在您的服务器中使用 set global wait_timeout=1*60*60
(时间以秒为单位)。
解决方案#2 - 在数据库空闲时间后强制建立新连接
在您的 Django 视图/任务中:
from django.db import connection
from django.db.utils import OperationalError
cursor = connection.cursor()
cursor.execute('SELECT 1') # <-- no issues here
# time consuming code...
try:
cursor.execute('SELECT 1')
except OperationalError:
connection.connect()
cursor = connection.cursor()
cursor.execute('SELECT 1')
观察:
这也应该适用于 ORM 请求
Django 3.1.6 版