OperationalError:MySQL连接不可用

时间:2013-09-27 14:14:31

标签: python mysql sqlalchemy flask flask-sqlalchemy

我正在使用Flask-SQLAlchemy 1.0,Flask 0.10,SQLAlchemy 0.8.2和Python 2.7.5。我正在使用Oracle的MySQL Connector / Python 1.0.12连接MySQL 5.6。

当我重新启动我的Web服务器(Apache2或Flask的内置)时,在OperationalError: MySQL Connection not available到期后(默认为8小时),我收到异常wait_timeout

我找到了similar problems人,并明确设置了SQLALCHEMY_POOL_RECYCLE = 7200,即使是Flask-SQLAlchemy's default。当我设置断点here时,我看到拆解功能在每次请求后成功调用session.remove()。有什么想法吗?

2014年7月21日更新:

由于这个问题一直受到关注,我必须补充一点,我做了尝试了一些提案。我的两次尝试看起来如下:

首先

@contextmanager
def safe_commit():
    try:
        yield
        db.session.commit()
    except:
        db.session.rollback()
        raise

这允许我像这样包装我的提交调用:

with safe_commit():
    model = Model(prop=value)
    db.session.add(model)

我99%肯定我没有错过使用此方法的任何db.session.commit来电,我仍然遇到问题。

第二

def managed_session():
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            try:
                response = f(*args, **kwargs)
                db.session.commit()
                return response
            except:
                db.session.rollback()
                raise
            finally:
                db.session.close()
        return decorated_function
    return decorator

为了进一步确保我没有错过任何提交调用,我创建了一个Flask包装器,启用了代码,如(如果我没记错的话):

@managed_session()
def hello(self):
    model = Model(prop=value)
    db.session.add(model)

    return render_template(...

不幸的是,两种方法都没有效果。我还记得尝试发出SELECT(1)调用以尝试重新建立连接,但我不再拥有该代码。

对我来说,底线是MySQL / SQL Alchemy有问题。当我迁移到Postgres时,我不必担心我的提交。一切正常。

3 个答案:

答案 0 :(得分:6)

我遇到了这个问题,这让我疯了。我尝试使用SQLALCHEMY_POOL_RECYCLE,但这似乎无法解决问题。

我终于找到http://docs.sqlalchemy.org/en/latest/orm/session.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it,并改编为flask-sqlalchemy。

在我开始使用以下模式后,我没有看到问题。关键似乎总是确保执行commit()rollback()。因此,如果if-then-else不流经commit()(例如,对于检测到的错误),则在重定向,中止,render_template调用之前也执行commit()rollback()。 / p>

class DoSomething(MethodView):
    def get(self):
        try:
            # do stuff
            db.session.commit()
            return flask.render_template('sometemplate.html')
        except:
            db.session.rollback()
            raise
app.add_url_rule('/someurl',view_func=DoSomething.as_view('dosomething'),methods=['GET'])

更新2014年7月22日

我发现我还必须将SQLALCHEMY_POOL_RECYCLE更改为小于MySQL interactive_timeout。在godaddy服务器上,interactive_timeout设置为60,所以我将SQLALCHEMY_POOL_RECYCLE设置为50.我认为我使用的模式和此超时都是使问题消失所必需的,但此时我并不积极。但是,我很确定当SQLALCHEMY_POOL_RECYCLE大于interactive_timeout时,我仍然遇到操作错误。

答案 1 :(得分:1)

我最近遇到了同样的问题 - 经过很长一段时间的FLASK&和MYSQL数据库之后的第一次请求。 SQLAlchemy应用程序不活动(至少8小时)会导致未处理的异常,这反过来意味着 500内部服务器错误:连接不可用。所有后续请求都没问题。

我设法将问题归结为MYSQL连接,将 @@ session.wait_timeout (以及 @@ global 以防万一)值减少到5秒。然后每个奇怪的请求都是正常的,而在5加一秒的停顿后每秒都失败了。结论很明显 - SQLAlchemy使用open,但是在数据库端连接上有时间。

解决方案

就我而言,事实证明该解决方案已在SQLAlchemy – MYSQL has gone away博文中详细说明:

  

首先要确定的是 pool_recycle 的值应该小于你的MYSQL wait_timeout 值。

在MYSQL文档中,您可以找到 wait_timeout 默认为8小时(28 800秒),而SQLAlchemy引擎的pool_recycle默认值为 -1 ,这不需要任何连接回收。我只是将21 600(6小时)的值传递给 create_engine 函数,错误就消失了。

答案 2 :(得分:0)

sqlalchemy提供了两种处理断开连接的方式,documentation

中的详细信息

简短版本:

  • 乐观

使用try...except块来捕获断开连接异常。这将在失败的请求上返回500,然后Web应用程序正常继续。如果不经常断开连接,请使用此方法。注意:您需要在try...except块中包装每个可能的失败操作。

  • 悲观(我正在使用的那个)

每次从池中签出连接时,基本上都会执行额外的ping操作(类似SELECT 1)。如果ping失败引发DisconnectionError,主机池将尝试强制创建新连接(事实上,池将在正式放弃之前尝试3次)。这样,您的应用程序就不会看到500错误。权衡是执行的额外SQL,尽管根据文档,开销很小。