连接未被关闭SQLAlchemy

时间:2015-05-04 17:51:19

标签: python mysql sqlalchemy

我最近在应用程序日志中看到了MySQL server has gone away我使用SQLAlchemy运行的守护进程。

我在装饰器中包装每个数据库查询或更新,该装饰器应在完成后关闭所有会话。从理论上讲,这也应该关闭connection

我的装饰师看起来像

  def dbop(meth):
    @wraps(meth)
    def nf(self, *args, **kwargs):
      self.session = self.sm()
      res = meth(self, *args, **kwargs)
      self.session.commit()
      self.session.close()
      return res
    return nf

我还使用以下命令在我的Python脚本顶部初始化数据库:

  def initdb(self):
    engine = create_engine(db_url)
    Base.metadata.create_all(engine)
    self.sm = sessionmaker(bind=engine,
                           autocommit=False,
                           autoflush=False,
                           expire_on_commit=False)

根据我的理解,我收到了这个错误,因为我的连接超时了。如果我在上面的装饰器中包装每个方法,为什么会这样呢?这是因为expire_on_commit即使在连接关闭后也会导致查询,并且可能会重新打开它们?这是因为Base.metadata.create_all导致执行SQL会打开一个未关闭的连接吗?

1 个答案:

答案 0 :(得分:0)

您的会话绑定到“引擎”,而“引擎”又使用连接池。每次SQLAlchemy都需要连接时,它会从池中检出一个连接,如果用它完成,它会返回到池,但它关闭!这是减少打开/关闭连接开销的常用策略。您在上面设置的所有选项仅对会话产生影响,而不会影响连接

默认情况下,池中的连接将无限期保持打开状态。

但MySQL会在一定程度的不活动后自动关闭连接(参见wait_timeout)。

这里的问题是MySQL服务器不会通知您的Python进程,如果连接处于非活动超时状态,则连接已关闭。相反, next 时间查询被发送到该连接,Python将发现该连接不再可用。如果由于其他原因导致连接丢失,也会发生类似的情况,例如强制服务重新启动,不等待打开的连接干净地关闭(例如,在postgres重新启动时使用“immediate”选项)。

这是您遇到异常的时候。

SQLAlchemy为您提供了各种处理此问题的策略,这些策略在@ lukas-graf提及的“Dealing with Disconnects”部分中有详细记录

如果你跳过一些箍,你可以获得对会话当前使用的连接的引用。您可以通过这种方式关闭它,但我强烈建议反对。相反,请参阅上面的“处理断开连接”会话,让SQLAlchemy透明地为您处理此问题。在您的情况下,设置pool_recycle option可能会解决您的问题。