34

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

视图.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()

我尝试了以下方法,但仍然无法正常工作:

视图.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()

目前,我唯一的解决方法是MySQLdb.connect()在每个使用 mysql 的函数中执行。我还注意到,当使用 django's 时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)
4

13 回答 13

52

有时如果您看到“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 或重新启动机器。

于 2013-07-22T22:40:05.090 回答
41

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

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

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

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

于 2013-01-10T22:26:47.097 回答
2

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

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

于 2018-10-12T15:36:38.740 回答
1

检查是否允许在一个线程中创建 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(...)
于 2013-01-09T10:48:49.423 回答
1

我也一直在为这个问题而苦苦挣扎。我不喜欢在 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 自动重新连接,但他们似乎拒绝了这个问题。

于 2016-01-06T18:34:37.690 回答
1

关于此类警告的最常见问题是您的应用程序已达到wait_timeoutMySQL 的值。

我在使用 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%';or SHOW VARIABLES LIKE 'interactive%';,为 , 配置的值interactive_timeout将出现在两个变量中,我不知道为什么,但事实是,为每个变量配置的值在 '/etc/mysql/mysql.conf.d/ mysqld.cnf',将被 MySQL 尊重。

于 2016-09-13T15:40:39.707 回答
1

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

所以我在调试模式下尝试了持久连接,查看链接: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
    },
于 2019-04-26T09:43:46.900 回答
1

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

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

于 2020-12-24T21:01:08.097 回答
0

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

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

于 2013-01-04T20:59:08.203 回答
0

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()
于 2013-07-22T15:10:39.617 回答
0

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

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

于 2016-05-04T14:40:23.773 回答
0

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

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

于 2016-10-31T12:34:19.343 回答
0

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

于 2017-01-16T07:14:31.770 回答