teardown_appcontext必须用于兜底清理数据库连接,它在每次应用上下文结束时(无论请求成功、异常或CLI/Celery等场景)必然触发,确保db.session.close()及时归还连接,避免QueuePool耗尽。

根本原因不是并发数高,而是连接没归还——db.session被线程本地的 scoped_session 持有,不显式清理就永远卡在池里。
teardown_appcontext 必须用,且不能写错位置
Flask 的 after_request 不保证执行(比如视图抛异常就跳过),teardown_request 也不覆盖 CLI 命令、Celery 任务、后台定时器这些路径。只有 @app.teardown_appcontext 能兜住所有上下文,包括异常退出。
- 必须写成:
@app.teardown_appcontext def shutdown_session(exception=None): db.session.close() -
db.session.remove()在 SQLAlchemy 1.4+ 已弃用,用close()才正确 - 别在
debug=True下测试——开发模式下异常会绕过teardown_appcontext,连接直接泄漏
pool_pre_ping 和 pool_recycle 必须配对启用
MySQL/PostgreSQL 连接可能被防火墙、DB 自动断开或网络闪断“静默杀死”,外表看连接还在,实际发 SQL 就卡死或报 Lost connection。默认 pool_pre_ping=False,池子不会主动探测,直到某次查询失败才踢掉它,期间所有等待该连接的请求都会阻塞。
- 启用:
SQLALCHEMY_ENGINE_OPTIONS = {"pool_pre_ping": True, "pool_recycle": 3600} -
pool_pre_ping会在每次取连接前发一条SELECT 1探活,RT 增加约 1–2ms,但避免整池卡死 -
pool_recycle=3600强制每小时重建连接,防止 DB 主动断开后连接滞留
max_overflow 不是救命稻草,设太高反而压垮数据库
看到 QueuePool limit of size 5 overflow 10 reached 就调大 max_overflow,是典型误判。每个 MySQL 连接占内存约 10–20MB,设成 100 就可能吃光 DB 内存,触发 Too many connections 或 CPU 飙升。
立即学习“Python免费学习笔记(深入)”;
- 先查真实连接数:
psql -c "SELECT count(*) FROM pg_stat_activity;"(PostgreSQL)或SHOW STATUS LIKE 'Threads_connected';(MySQL) - 合理值参考:Web 应用
pool_size=10+max_overflow=5足够支撑数百 QPS - 如果压测中连接数持续上涨,说明有泄漏,不是池子小——优先检查
teardown_appcontext是否生效
异步协程或 Celery 任务要单独初始化 session
Flask 的 db.session 绑定应用上下文,而 Celery worker、后台线程、async def 视图都不在 Flask 上下文中,直接调用 User.query.all() 会报 Working outside of application context,更糟的是——有些框架(如旧版 flask-sqlalchemy)会悄悄新建 session 却不归还,导致连接无声泄漏。
- Celery 任务里必须手动 push context:
with app.app_context(): users = User.query.all() - 异步视图中不要混用同步 session,改用
asyncpg或aiomysql等原生异步驱动 - CLI 命令(
flask shell)也要确保 session 清理,teardown_appcontext对它同样有效
最隐蔽的坑是:连接泄漏往往不立刻暴露,而是在凌晨流量低谷时因长连接超时、DB 重启或网络抖动集中爆发。别等报错才查,上线前就该用 echo_pool="debug" 观察连接获取/归还日志。


















