Python 和 Django OperationalError (2006, 'MySQL server has gone away')

Posted

技术标签:

【中文标题】Python 和 Django OperationalError (2006, \'MySQL server has gone away\')【英文标题】:Python and Django OperationalError (2006, 'MySQL server has gone away')Python 和 Django OperationalError (2006, 'MySQL server has gone away') 【发布时间】:2012-12-19 06:18:16 【问题描述】:

原文:我最近开始从我的一些旧代码中获取 mysql OperationalErrors,并且似乎无法追溯问题。由于它以前可以工作,我认为它可能是软件更新破坏了某些东西。我正在将 python 2.7 与 django runfcgi 与 nginx 一起使用。这是我的原始代码:

views.py

DBNAME = "test"
DBIP = "localhost"
DBUSER = "django"
DBPASS = "password"
db = MySQLdb.connect(DBIP,DBUSER,DBPASS,DBNAME)
cursor = db.cursor()

def list(request):
    statement = "SELECT item from table where selected = 1"
    cursor.execute(statement)
    results = cursor.fetchall()

我尝试了以下方法,但还是不行:

views.py

class DB:
    conn = None
    DBNAME = "test"
    DBIP = "localhost"
    DBUSER = "django"
    DBPASS = "password"
def connect(self):
    self.conn = MySQLdb.connect(DBIP,DBUSER,DBPASS,DBNAME)
def cursor(self):
    try:
        return self.conn.cursor()
    except (AttributeError, MySQLdb.OperationalError):
        self.connect()
        return self.conn.cursor()

db = DB()
cursor = db.cursor()

def list(request):
    cursor = db.cursor()
    statement = "SELECT item from table where selected = 1"
    cursor.execute(statement)
    results = cursor.fetchall()

目前,我唯一的解决方法是在每个使用 mysql 的函数中执行MySQLdb.connect()。我还注意到,当使用 django 的 manage.py runserver 时,我不会遇到这个问题,而 nginx 会抛出这些错误。我怀疑我的连接超时了,因为list() 在启动服务器的几秒钟内被调用。我正在使用的软件是否有任何更新会导致此问题/是否有任何解决方法?

编辑:我意识到我最近编写了一个中间件来守护一个函数,这就是问题的原因。但是,我不知道为什么。这是中间件的代码

def process_request_handler(sender, **kwargs):
    t = threading.Thread(target=dispatch.execute,
        args=[kwargs['nodes'],kwargs['callback']],
        kwargs=)
    t.setDaemon(True)
    t.start()
    return
process_request.connect(process_request_handler)

【问题讨论】:

你有一个名为list的本地函数?这是个坏主意。 在一些数据库适配器/库中有一个自动重新连接的设置,类似于 mysql 客户端处理超时的方式。看看你是否可以设置它。 【参考方案1】:

这段代码有多少年了?至少从 0.96 开始,Django 就在设置中定义了数据库。我能想到的唯一其他事情是多数据库支持,这改变了一些事情,但即使是 1.1 或 1.2。

即使您需要为某些视图使用特殊的数据库,我认为您最好在设置中定义它。

【讨论】:

圣诞节前一周我去度假之前它正在工作【参考方案2】:

检查是否允许在一个线程中创建 mysql 连接对象,然后在另一个线程中使用它。

如果禁止,请使用 threading.Local 进行每线程连接:

class Db(threading.local):
    """ thread-local db object """
    con = None

    def __init__(self, ...options...):
        super(Db, self).__init__()
        self.con = MySQLdb.connect(...options...)

db1 = Db(...)


def test():
    """safe to run from any thread"""
    cursor = db.con.cursor()
    cursor.execute(...)

【讨论】:

游标在线程本身(在dispatch.execute中)和views.py中定义。如果线程无法访问它,我会得到一个不同的错误。【参考方案3】:

根据MySQL documentation,当客户端无法向服务器发送问题时会引发错误消息,这很可能是因为服务器本身已关闭连接。在最常见的情况下,服务器将在(默认)8 小时后关闭空闲连接。这可以在服务器端进行配置。

MySQL documentation 提供了许多其他可能的原因,可能值得研究一下,看看它们是否适合您的情况。

在每个函数中调用connect() 的替代方法(最终可能会不必要地创建新连接)是在连接对象上使用ping() 方法进行调查;这将使用尝试自动重新连接的选项来测试连接。我努力在网上为ping() 方法找到一些decent documentation,但this question 的答案可能会有所帮助。

请注意,在处理事务时自动重新连接可能很危险,因为重新连接似乎会导致隐式回滚(这似乎是自动重新连接不是 MySQLdb 实现功能的主要原因)。

【讨论】:

在运行查询之前 ping 检查连接是否处于活动状态的建议被认为是一种浪费资源且不可靠的反模式:percona.com/blog/2010/05/05/… 感谢您的链接,杰夫。我同意他们在那里提供的方法(自动重试)更好。【参考方案4】:

SQLAlchemy 现在有一篇关于如何使用 ping 对连接的新鲜度感到悲观的文章:

http://docs.sqlalchemy.org/en/latest/core/pooling.html#disconnect-handling-pessimistic

从那里,

from sqlalchemy import exc
from sqlalchemy import event
from sqlalchemy.pool import Pool

@event.listens_for(Pool, "checkout")
def ping_connection(dbapi_connection, connection_record, connection_proxy):
    cursor = dbapi_connection.cursor()
    try:
        cursor.execute("SELECT 1")
    except:
        # optional - dispose the whole pool
        # instead of invalidating one at a time
        # connection_proxy._pool.dispose()

        # raise DisconnectionError - pool will try
        # connecting again up to three times before raising.
        raise exc.DisconnectionError()
    cursor.close()

并进行测试以确保上述工作正常:

from sqlalchemy import create_engine
e = create_engine("mysql://scott:tiger@localhost/test", echo_pool=True)
c1 = e.connect()
c2 = e.connect()
c3 = e.connect()
c1.close()
c2.close()
c3.close()

# pool size is now three.

print "Restart the server"
raw_input()

for i in xrange(10):
    c = e.connect()
    print c.execute("select 1").fetchall()
    c.close()

【讨论】:

【参考方案5】:

有时如果您看到“OperationalError: (2006, 'MySQL server has gone away')”,那是因为您发出的查询太大。例如,如果您将会话存储在 MySQL 中,并且您试图在会话中放入一些非常大的东西,就会发生这种情况。要解决此问题,您需要增加 MySQL 中 max_allowed_pa​​cket 设置的值。

默认值为 1048576。

所以查看当前值为默认值,运行以下SQL:

select @@max_allowed_packet;

要临时设置一个新值,请运行以下 SQL:

set global max_allowed_packet=10485760;

要更永久地解决问题,请创建一个至少包含以下内容的 /etc/my.cnf 文件:

[mysqld]
max_allowed_packet = 16M

编辑 /etc/my.cnf 后,如果你不知道怎么做,你需要重启 MySQL 或重启你的机器。

【讨论】:

为我工作!谢谢 “如果您不知道如何重启您的机器。”....“您好,IT...您是否尝试将其关闭再打开?” 配置文件的位置可能是/etc/mysql/mysql.conf/mysqld.cnf【参考方案6】:

我也一直在努力解决这个问题。我不喜欢在 mysqlserver 上增加超时的想法。正如提到的那样,使用CONNECTION_MAX_AGE 自动重新连接也不起作用。不幸的是,我最终包装了像这样查询数据库的每个方法

def do_db( callback, *arg, **args):
    try:
        return callback(*arg, **args)
    except (OperationalError, InterfaceError) as e:  # Connection has gone away, fiter it with message or error code if you could catch another errors
        connection.close()
        return callback(*arg, **args)

do_db(User.objects.get, id=123)  # instead of User.objects.get(id=123)

如您所见,我宁愿捕获异常,也不愿每次在查询之前 ping 数据库。因为捕获异常是一种罕见的情况。我希望 django 自动重新连接,但他们似乎 refused 那个问题。

【讨论】:

【参考方案7】:

我遇到了这个问题,并且无法更改我的配置。我终于发现问题出在我的 50000 条记录循环中出现了 49500 条记录,因为那是我再次尝试(在很久以前尝试过)访问我的第二个数据库的时候。

所以我更改了我的代码,以便每隔几千条记录,我再次访问第二个数据库(使用一个非常小的表的 count()),并且修复了它。毫无疑问,“ping”或其他一些接触数据库的方法也可以。

【讨论】:

【参考方案8】:

有关此类警告的最常见问题是,您的应用程序已达到 MySQL 的 wait_timeout 值。

我在使用 Flask 应用时遇到了同样的问题。

我是这样解决的:

$ grep timeout /etc/mysql/mysql.conf.d/mysqld.cnf 
# https://support.rackspace.com/how-to/how-to-change-the-mysql-timeout-on-a-server/
# wait = timeout for application session (tdm)
# inteactive = timeout for keyboard session (terminal)
# 7 days = 604800s / 4 hours = 14400s 
wait_timeout = 604800
interactive_timeout = 14400

观察:如果您通过 MySQL 批处理模式搜索变量,值将按原样显示。但是如果你执行SHOW VARIABLES LIKE 'wait%';SHOW VARIABLES LIKE 'interactive%';,为interactive_timeout 配置的值将出现在两个变量中,我不知道为什么,但事实是,在'/etc 中为每个变量配置的值/mysql/mysql.conf.d/mysqld.cnf',将被 MySQL 尊重。

【讨论】:

【参考方案9】:

这个错误很神秘,因为 MySQL 没有报告它断开连接的原因,它就消失了。

看来这种断线的原因有很多。我刚刚发现的一个是,如果查询字符串太大,服务器会断开连接。这可能与max_allowed_packets 设置有关。

【讨论】:

【参考方案10】:

首先,您应该确保 MySQL 会话和全局环境 wait_timeoutinteractive_timeout 值。其次,您的客户端应尝试重新连接到低于这些环境值的服务器。

【讨论】:

【参考方案11】:

这可能是由于数据库连接从主线程复制到您的子线程中。在使用 python 的多处理库生成不同的进程时,我遇到了同样的错误。连接对象在分叉期间在进程之间复制,在子线程中进行数据库调用时会导致 MySQL OperationalErrors。

这里有一个很好的参考来解决这个问题:Django multiprocessing and database connections

【讨论】:

【参考方案12】:

对我来说,这是在调试模式下发生的。

所以我在调试模式下尝试了持久连接,查看链接:Django - Documentation - Databases - Persistent connections。

在设置中:

'default': 
       'ENGINE': 'django.db.backends.mysql',
       'NAME': 'dbname',
       'USER': 'root',
       'PASSWORD': 'root',
       'HOST': 'localhost',
       'PORT': '3306',
       'CONN_MAX_AGE': None
    ,

【讨论】:

【参考方案13】:

当您在不进入数据库的耗时操作后尝试使用连接时,可能会出现此错误。由于连接有一段时间没有使用,MySQL 超时,连接被静默丢弃。

您可以尝试在耗时的非数据库操作之后调用close_old_connections(),以便在连接不可用时打开新连接。请注意,如果您有交易,请不要使用close_old_connections()

【讨论】:

以上是关于Python 和 Django OperationalError (2006, 'MySQL server has gone away')的主要内容,如果未能解决你的问题,请参考以下文章

Python DB operation

运用循环求和( sum operation in python)

django-xadmin常用内容记录

python file_operation

python 蟒蛇:string_operation

Mac下编译Thrift的时候Python2.7会报错 site-packages': Operation not permitted