【问题标题】:SQLAlchemy error MySQL server has gone awaySQLAlchemy 错误 MySQL 服务器已消失
【发布时间】:2013-04-26 20:29:14
【问题描述】:

错误OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 我在 Flask 上编写项目时已经收到此错误,但我不明白为什么会收到此错误。

我有这样的代码(是的,如果代码小且执行速度快,那么没有错误)\

db_engine = create_engine('mysql://root@127.0.0.1/mind?charset=utf8', pool_size=10, pool_recycle=7200)
Base.metadata.create_all(db_engine)

Session = sessionmaker(bind=db_engine, autoflush=True)
Session = scoped_session(Session)
session = Session()

# there many classes and functions

session.close()

这段代码返回错误'MySQL server has gone away',但一段时间后,当我在脚本中使用暂停时返回它。

我从 openserver.ru 使用 Mysql(它是 web 服务器,例如 wamp)。

谢谢..

【问题讨论】:

  • 我认为,我收到此错误是因为我的脚本中有逻辑错误...
  • 我认为当这种情况发生时你必须重新创建引擎或 sessionmaker,但我仍在调查中。

标签: python mysql sqlalchemy flask


【解决方案1】:

要记住的另一点是手动推送带有数据库初始化的烧瓶应用程序上下文。这应该可以解决问题。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

app = Flask(__name__)
with app.app_context():
    db.init_app(app)

【讨论】:

    【解决方案2】:

    https://docs.sqlalchemy.org/en/latest/core/pooling.html#disconnect-handling-optimistic

     def sql_read(cls, sql, connection):
        """sql for read action like select
        """
        LOG.debug(sql)
        try:
            result = connection.engine.execute(sql)
            header = result.keys()
            for row in result:
                yield dict(zip(header, row))
        except OperationalError as e:
            LOG.info("recreate pool duo to %s" % e)
            connection.engine.pool.recreate()
            result = connection.engine.execute(sql)
            header = result.keys()
            for row in result:
                yield dict(zip(header, row))
        except Exception as ee:
            LOG.error(ee)
            raise SqlExecuteError()
    

    【讨论】:

      【解决方案3】:

      查看mysql docs,我们可以看到有一个bunch of reasons why this error can occur。但是,我看到的两个主要原因是:


      1) 最常见的原因是超过 8 小时未使用连接已断开连接(默认设置)

      默认情况下,如果没有发生任何事情,服务器会在八小时后关闭连接。启动mysqld时可以通过设置wait_timeout变量来改变时间限制

      为了完整起见,我将仅提及处理该问题的两种方法,但其他答案中已经提到过它们:

      A:我的工作时间很长,所以我的连接已经过时了。为了解决这个问题,我刷新了我的连接:

      create_engine(conn_str, pool_recycle=3600)  # recycle every hour
      

      B:我有一个长期运行的服务和长期不活动。为了解决这个问题,我在每次调用之前 ping mysql:

      create_engine(conn_str, pool_pre_ping=True)
      

      2)我的数据包太大,应该会抛出这个错误:

      _mysql_exceptions.OperationalError: (1153, "Got a packet bigger than 'max_allowed_packet' bytes")
      

      我只看到这个隐藏在跟踪的中间,虽然通常你只会看到通用的_mysql_exceptions.OperationalError (2006, 'MySQL server has gone away'),所以很难捕捉到,尤其是如果日志位于多个位置。

      The above doc说最大包大小默认是64MB,其实是16MB,可以用SELECT @@max_allowed_packet验证

      要解决此问题,请减小 INSERTUPDATE 调用的数据包大小。

      【讨论】:

      • 其他答案也很好,但你说的是“为什么”,它比其他答案更有帮助,谢谢
      【解决方案4】:

      我刚遇到同样的问题,经过一番努力就解决了。希望我的经验对其他人有所帮助。

      考虑到一些建议,我使用了连接池并将pool_recycle 设置为小于wait_timeout,但它仍然不起作用。

      然后,我意识到全局会话可能只是使用相同的连接,而连接池不起作用。为避免全局会话,为每个请求生成一个新会话,处理后由Session.remove() 删除。

      终于,一切都好。

      【讨论】:

        【解决方案5】:

        documentation你可以使用pool_recycle参数:

        from sqlalchemy import create_engine
        e = create_engine("mysql://scott:tiger@localhost/test", pool_recycle=3600)
        

        【讨论】:

        • OP 已经在使用这个参数了。
        【解决方案6】:

        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()
        

        【讨论】:

          猜你喜欢
          • 2021-02-09
          • 1970-01-01
          • 2011-04-25
          • 1970-01-01
          • 2012-06-28
          • 2011-12-18
          • 2013-12-31
          相关资源
          最近更新 更多