Django,升级后:MySQL服务器已经消失了



>我最近从 Django 1.4 升级到 Django 1.7,由于我不断收到某些脚本的以下错误消息,有时:

OperationalError: (2006, 'MySQL server has gone away')

脚本是非常长或连续运行的任务,可能涉及几分钟不与数据库通信的阶段,因此连接超时。然而,在我升级之前,这没有问题,因为 Django 似乎会自动重新建立连接。现在没有了,这意味着任务经常在中间停止并失败。

有谁知道发生了什么变化以及我如何解决它?

是否可能与该票证/修复有关:https://code.djangoproject.com/ticket/21463

多谢!

这种行为的原因是持久连接到数据库,这是在 Django 1.6 中引入的。

为防止连接超时错误,您应该在 settings.py 中将 CONN_MAX_AGE 设置为 MySQL 配置(my.cnf)中小于 wait_timeout 的值。在这种情况下,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

我有一个正在运行的后台进程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 票证 15119 中所述,我们可以看到 Django 在执行每个查询之前正在执行 ping 以验证连接是否处于活动状态。此行为已在提交 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()方法中。

在 Django 1.9 上:我有一个正在进行的Django Shell在Unix屏幕中运行,无人看管超过48小时。当我回到它并运行一个<some_model>.objects.filter它扔掉时 OperationalError: (2006, 'MySQL server has gone away')

一个快速的import django.db; django.db.close_old_connections()为我做了这个技巧。

我找不到 1.9 版 Django Docs 上的close_old_connections()文档,但这里是它在 Github 上的 Django 代码库中实现的直接链接

在 django 1.6 中,当wait_timeout通过(mysql)时,数据库访问会导致(2006,'MySQL 服务器已经消失')错误。在 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())

工作正常。

我们也注意到了这一点。上面的答案是将CONN_MAX_AGE设置为小于MySQL/MariaDB的wait_timeout工作 - 对于网络。

但是,对于长时间运行的任务,这似乎不起作用。相反,我们包装它并在每次执行长时间运行的任务之一时关闭连接。

我们将此与我们自己的自定义池相结合。接受它或离开它 - 默认的 Django 控制为零 - 不是我们在生产中喜欢的东西。我们设置了一个最大池,以便在服务器终止连接过多的数据库之前杀死服务器。将其用作任务的装饰器:

@close_db_connection()
def task_do_something():
    print 'Hello'

'''
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

简短:

  1. pip install mysql_server_has_gone_away

  2. settings.py

DATABASES = {
    'default': {
        'ENGINE': 'mysql_server_has_gone_away'
    }
}

博士

就我而言,我正在使用具有长寿命请求(webocket)的django ORM。 CONN_MAX_AGE在我的情况下不起作用。

一开始我创建了一个包装器,它会尝试捕获错误,但由于 django 延迟加载,不清楚你应该包装它是为了哪些事情。所以我最终在整个项目中复制了这段代码,这是一种痛苦。而不是写例如 User.objects.get(id=3)我会做do_db(User.objects.get, id=3),数据库try: return callback(*args, **kwargs); catch e: conn.close(); callback(*args, **kwargs).

在深入研究 django 后端时,我们可以在连接级别迁移此解决方案。因此,每个转到 db 的查询都将用它包装:

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'lol'
    }
}

lol/base.py

""" 
https://stackoverflow.com/a/60894948/3872976
"""
from django.db.backends.mysql import base

def check_mysql_gone_away(db_wrapper):
    def decorate(f):
        def wrapper(self, query, args=None):
            try:
                return f(self, query, args)
            except (base.Database.OperationalError, base.Database.InterfaceError) as e:
                if 'MySQL server has gone away' in str(e):
                    db_wrapper.connection.close()
                    db_wrapper.connect()
                    self.cursor = db_wrapper.connection.cursor()
                    return f(self, query, args)
                # Map some error codes to IntegrityError, since they seem to be
                # misclassified and Django would prefer the more logical place.
                if e.args[0] in self.codes_for_integrityerror:
                    raise base.utils.IntegrityError(*tuple(e.args))
                raise
        return wrapper
    return decorate

class DatabaseWrapper(base.DatabaseWrapper):
    def create_cursor(self, name=None):
        class CursorWrapper(base.CursorWrapper):
            @check_mysql_gone_away(self)
            def execute(self, query, args=None):
                return self.cursor.execute(query, args)
            @check_mysql_gone_away(self)
            def executemany(self, query, args):
                return self.cursor.executemany(query, args)
        cursor = self.connection.cursor()
        return CursorWrapper(cursor)

您应该注意,如果您在原子操作期间mysql断开连接,则会遇到事务问题。但不幸的是,没有其他办法可以解决这个问题。

也许超时对某些人来说是一个问题,但是在尝试编写非常大的BLOB字段时,我遇到了这个问题。 我通过增加 mysql 配置文件中允许的最大数据包大小来解决这个问题......

max_allowed_packet=4M

不要忘记在更改为/etc/my.cnf 后重新启动 mysql。 此页面帮助...

http://dev.mysql.com/doc/refman/5.5/en/packet-too-large.html

我面临着同样的问题,我的解决方案在哪里。我是 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选项,它现在运行良好。

我知道

这是一个老问题,但我的解决方案不在当前的任何答案中。

我问题的根源在服务器端。通过检查服务器超时设置(使用 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请求
姜戈版本 3.1.6

相关内容

  • 没有找到相关文章

最新更新