我最近在应用程序日志中看到了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会打开一个未关闭的连接吗?
答案 0 :(得分:0)
您的会话绑定到“引擎”,而“引擎”又使用连接池。每次SQLAlchemy都需要连接时,它会从池中检出一个连接,如果用它完成,它会返回到池,但它不关闭!这是减少打开/关闭连接开销的常用策略。您在上面设置的所有选项仅对会话产生影响,而不会影响连接!
默认情况下,池中的连接将无限期保持打开状态。
但MySQL会在一定程度的不活动后自动关闭连接(参见wait_timeout)。
这里的问题是MySQL服务器不会通知您的Python进程,如果连接处于非活动超时状态,则连接已关闭。相反, next 时间查询被发送到该连接,Python将发现该连接不再可用。如果由于其他原因导致连接丢失,也会发生类似的情况,例如强制服务重新启动,不等待打开的连接干净地关闭(例如,在postgres重新启动时使用“immediate”选项)。
这是您遇到异常的时候。
SQLAlchemy为您提供了各种处理此问题的策略,这些策略在@ lukas-graf提及的“Dealing with Disconnects”部分中有详细记录
如果你跳过一些箍,你可以获得对会话当前使用的连接的引用。您可以通过这种方式关闭它,但我强烈建议反对。相反,请参阅上面的“处理断开连接”会话,让SQLAlchemy透明地为您处理此问题。在您的情况下,设置pool_recycle option可能会解决您的问题。