我正在运行PostgreSQL 9.3和SQLAlchemy 0.8.2并且遇到数据库连接漏洞。部署应用程序后,消耗大约240个连接。在接下来的30个小时内,当PostgreSQL开始断开连接时,这个数字会逐渐增加到500。
我使用SQLAlchemy thread-local sessions:
from sqlalchemy import orm, create_engine
engine = create_engine(os.environ['DATABASE_URL'], echo=False)
Session = orm.scoped_session(orm.sessionmaker(engine))
对于Flask网络应用,.remove()
代理对象的Session
调用将在请求拆除期间发送:
@app.teardown_request
def teardown_request(exception=None):
if not app.testing:
Session.remove()
这应该与Flask-SQLAlchemy
正在做的事情相同。
我还有一些在循环中运行的周期性任务,我为循环的每次迭代调用.remove()
:
def run_forever():
while True:
do_stuff(Session)
Session.remove()
我做错了什么可能导致连接泄漏?
答案 0 :(得分:3)
如果我在使用SQLAlchemy的实验中记得正确,scoped_session()
用于创建可以从多个位置访问的会话。也就是说,您在一个方法中创建会话并在另一个方法中使用它而不显式传递会话对象。
它通过保留会话列表并将它们与“范围ID”相关联来实现。默认情况下,要获取范围ID,它使用当前线程ID;所以你有每个线程的会话。您可以提供scopefunc
来提供 - 例如 - 每个请求一个ID:
# This is (approx.) what flask-sqlalchemy does:
from flask import _request_ctx_stack as context_stack
Session = orm.scoped_session(orm.sessionmaker(engine),
scopefunc=context_stack.__ident_func__)
另外,请注意有关执行后台任务的其他答案和评论。
答案 1 :(得分:0)
首先,运行后台任务真是一个非常糟糕的方法。尝试像芹菜这样的任何ASync调度程序。
不是100%肯定所以根据提供的信息这是一个猜测,但我想知道每个页面加载是否正在启动新的数据库连接,然后正在侦听通知。如果是这种情况,我想知道数据库连接是否已从池中有效删除,因此会在下一页加载时创建。
如果是这种情况,我的建议是拥有一个专门用于侦听通知的单独DBI数据库句柄,以便它们在队列中不活动。这可能在您的工作流程之外完成。
另外
特别是,在发出多个同时请求时会发生泄漏。与此同时,我可以看到一些请求留下了未完成的查询执行和超时。您可以自己写一些东西来管理它。