13

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

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

我发现有类似问题的人并明确设置SQLALCHEMY_POOL_RECYCLE = 7200了 ,即使那是Flask-SQLAlchemy 的默认值。当我在此处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 时,我不必担心我的提交。一切都刚刚好。

4

3 回答 3

8

我遇到了这个问题,这让我发疯了。我尝试使用 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,并适用于烧瓶 sqlalchemy。

在我开始使用以下模式后,我还没有看到问题。关键似乎总是确保 a commit()orrollback()被执行。因此,如果有 if-then-else 没有通过commit()(例如,对于检测到的错误),也要在重定向、中止、render_template 调用之前执行commit()或执行。rollback()

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 时,我仍然会遇到操作错误。

于 2014-07-21T12:49:23.133 回答
2

我最近遇到了同样的问题 - 在 FLASK 和 SQLAlchemy 应用程序长时间不活动(至少 8 小时)后对 MYSQL 数据库的第一次请求导致未处理的异常,这反过来意味着500 Internal Server Error: Connection Unavailable。所有后续请求都很好。

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

解决方案

在我的情况下,结果证明解决方案在SQLAlchemy 中详细说明 - MYSQL has gone away blog post:

首先要确定的是 [...] pool_recycle 的值应该小于您的 MYSQLs wait_timeout值。

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

于 2017-09-01T14:20:26.770 回答
1

sqlalchemy提供 2 种处理断开连接的方法,文档中的详细信息

精简版:

  • 乐观地

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

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

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

于 2014-03-12T12:38:01.473 回答