You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用sessionmaker封装函数时出现数据库连接过多问题的原因排查

使用sessionmaker封装函数时出现数据库连接过多问题的原因排查

核心问题:lru_cache误用导致Session实例被全局缓存

你的问题根源在于get_session()函数被@functools.lru_cache(maxsize=None)装饰了——这个装饰器会永久缓存函数的返回结果,意味着每次调用get_session()都会返回同一个Session实例,而不是每次都创建全新的会话对象。

详细分析流程

  1. 第一次调用get_session():创建新的Session实例并缓存它,在with语句中使用后,会话被关闭,连接理论上应该被释放回连接池。
  2. 后续调用get_session():由于缓存机制,直接返回之前已经被关闭的Session实例。当你再次用with包裹这个实例时,SQLAlchemy会尝试重新激活它,但这个过程会绕过正常的连接池管理逻辑,导致旧连接没有被正确回收,甚至每次复用都会创建新的连接却不释放,最终造成连接数持续增长。

对比你无包装器的代码myfun_withoutwrapper():每次都会创建全新的Session实例,with语句退出时会正确关闭会话并将连接归还到连接池,所以不会出现连接泄漏。

验证方法

你可以在get_session()中添加打印语句,确认每次返回的是同一个实例:

@functools.lru_cache(maxsize=None)
def get_session() -> Session:
    sess = _get_sessionmaker()()
    print(f"Session instance ID: {id(sess)}")
    return sess

调用多次myfun()后,你会发现打印的ID完全相同,证明始终在复用同一个Session对象。

解决方案

  • 移除get_session()上的lru_cache装饰器:Session是有状态的会话对象,必须随用随创建,用完即销毁(通过with自动处理),绝对不能全局缓存。
  • 保留_get_sessionmaker()和get_engine()的缓存:这两个函数返回的是无状态的工厂类和引擎实例,缓存它们是合理的,可以避免重复创建引擎和会话工厂,提升性能。

修改后的get_session()应该是这样:

def get_session() -> Session:
    return _get_sessionmaker()()

这样每次调用get_session()都会返回全新的Session实例,with语句退出时也能正确关闭会话、释放连接回连接池,解决连接数过多的问题。

备注:内容来源于stack exchange,提问作者duepiert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 11:22:59