超时重新连接 MySQL
Posted
技术标签:
【中文标题】超时重新连接 MySQL【英文标题】:Reconnecting MySQL on timeout 【发布时间】:2014-03-18 03:31:24 【问题描述】:我有一个 Python 程序,它在后台运行数周,并且每隔一段时间进行一次数据库查询。为此,我正在使用 ORM peewee
(版本 2.2.1)。我使用mysql
作为后端。
最近我在访问数据库时遇到了一个反复出现的问题,通常是在运行程序几天之后。 peewee
引发的错误是
peewee.OperationalError: (2006, 'MySQL server has gone away')
回溯在peewee
深处。我把它贴在这里,但由于我的virtualenv
使文件名太长,我正在缩短它们:
File ".../local/lib/python2.7/site-packages/peewee.py", line 2910, in save
ret_pk = self.insert(**field_dict).execute()
File ".../local/lib/python2.7/site-packages/peewee.py", line 2068, in execute
return self.database.last_insert_id(self._execute(), self.model_class)
File ".../local/lib/python2.7/site-packages/peewee.py", line 1698, in _execute
return self.database.execute_sql(sql, params, self.require_commit)
File ".../local/lib/python2.7/site-packages/peewee.py", line 2232, in execute_sql
self.commit()
File ".../local/lib/python2.7/site-packages/peewee.py", line 2104, in __exit__
reraise(new_type, new_type(*exc_value.args), traceback)
File ".../local/lib/python2.7/site-packages/peewee.py", line 2223, in execute_sql
res = cursor.execute(sql, params or ())
File ".../local/lib/python2.7/site-packages/MySQLdb/cursors.py", line 205, in execute
self.errorhandler(self, exc, value)
File ".../local/lib/python2.7/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
raise errorclass, errorvalue
peewee.OperationalError: (2006, 'MySQL server has gone away')
我发现的可能的解决方案尝试:
在this question 中,其中一位cmets 建议每隔一段时间对MySQL
服务器进行一次ping 操作,以使其(连接?)保持活动状态。不过,我不确定如何通过 ORM 做到这一点。 (我应该每小时简单地SELECT 1
吗?)
在 4 个月前打开的 this github peewee issue 中,提到了相同的错误,但声称已解决(而且我使用的是更新版本)。
在trac
的7 year old issue 中,一个建议是将MySQL
的超时时间延长3 天。
在此forum discussion 中,建议选择增加MySQL
的超时时间,但提供了“使用 MySQL JDBC 连接器的 autoReconnect 选项”的替代方案。我试图弄清楚 Python 的 MySQLdb
模块是否存在这样的选项,但找不到。
我在重新连接行为上找到了这个MySQL reference page,但是对于我对MySQL
的理解来说有点复杂(通常我只使用ORM),我不知道如何从@987654341 应用它@。
即使我能够 ping 数据库以使连接保持更长时间的活动,我认为在不需要连接时保持连接活动被认为是一种不好的做法。有没有办法通过 ORM 重新打开连接?我认为 ping 和增加 MySQL
的超时作为解决方法,而真正的解决方案是在需要时重新连接(真正的解决方案是我所要求的)。
【问题讨论】:
查看MySQL中wait_timeout
的值
我猜是8小时;这不是默认的吗?
连接完成后只需手动调用db.close()
。
我从来没有“完成”它;该程序只是继续运行。尽管如此,如果我不使用连接,我认为保持连接不是一个好习惯。我设置的任何超时都可能不够大。
【参考方案1】:
您必须捕获异常并根据哪个错误重新连接或执行其他操作。无论是连接超时,还是网络问题或 MySQL 必须重新启动。
下面的(伪)代码显示了如何做到这一点,但还有更多。您可能想尝试几次然后退出,或者每 2 分钟左右尝试一次。
while True:
try:
# do your database stuff
except peewee.OperationalError as exc:
# Oops! We have to try to reconnect
是否使用 ORM 并不重要。但是,ORM 可能会提供此功能。
【讨论】:
这看起来确实是正确的做法。然而,这并不容易完成;没有一种方法可以让我用这样的try... except
结构进行包装,每次从不同的地方引发错误。这就是 ORM 的好处——但也存在问题。它们在您的代码中变得几乎透明。无论如何,我最终 ping 了服务器,希望 peewee
有一天人们会实现一个内部可选的重新连接方法。
在这种极端情况下,您可以将 try/except 放在脚本的 main() 方法周围。
不,我永远不会那样做......它不是脚本,它是一个巨大的多线程软件......我只是声称这是ORM的责任,否则我必须“入侵”它的领土,使其变得多余。
我发现通用的“重新连接并重试”方法并不总是有效。特别是如果您正在重试的查询是准备好的语句。如果已准备好,则需要在建立连接后“重新准备”,这可能比您想象的要复杂,具体取决于现有的抽象。【参考方案2】:
我已经解决了这个问题。
我的解决方案是使用来自playhouse.pool
模块的mysql连接池PooledMySQLDatabase
。
请阅读:https://github.com/coleifer/peewee/issues/239
from peewee import *
from playhouse.pool import *
【讨论】:
附注:此模块被认为是实验性的,将其应用于生产可能会很危险。【参考方案3】:我遇到了同样的问题,对于使用 MySQLdb 的 peewee,我在初始化 MySQL 数据库实例时得到了以下解决方案:
db = MySQLDatabase(db_name, user=db_username, passwd=db_password, host=db_host, port=db_port)
db.get_conn().ping(True)
ping 函数的位置:
检查与服务器的连接是否正常。如果它 已关闭,尝试自动重新连接。
此功能可供长期闲置的客户端使用 while,检查服务器是否关闭了连接 并在必要时重新连接。
1.2.2 中的新功能:接受可选的重新连接参数。如果为真,那么 客户端将尝试重新连接。请注意,此设置是 执着的。默认情况下,在 MySQL
非标准。您应该假设 ping() 执行隐式 回滚;仅在开始新事务时使用。你已经 警告。
在db.get_conn().ping.__doc__
。请注意,如果您再次创建另一个连接,则必须使用 db.get_conn().ping(True)
。因此,如果您重新连接(例如通过 db.connect()
),您必须重复 ping。
【讨论】:
在运行查询之前 Ping 被认为是一种浪费资源且不可靠的反模式:percona.com/blog/2010/05/05/… 每次 ping 数据库,都会杀死一只可爱的海豚。以上是关于超时重新连接 MySQL的主要内容,如果未能解决你的问题,请参考以下文章