bigywater.tech
Flask · 92

Pool Checkout 与连接释放

一个请求到底占着数据库连接多久?什么写法会让连接泄漏?为什么请求里的慢调用会拖垮连接池?这篇用 pool.checkedout() 和池事件,把连接的借出与归还一步步量出来。

2195 → 1182 ms
慢操作之前不 commit 与 commit 之后,3 个并发请求(pool_size=2)的总耗时
2 个
不关闭 connect() 的请求之后,借出的连接数(pool_size=2)
0 → 1
视图里查询与生成器里查询时,流式响应期间借出的连接数

借出与归还

实测 · 本机执行输出
  开始                           借出的连接数 = 0
  执行 select 1                  借出的连接数 = 1
  commit()                     借出的连接数 = 0
  再执行 select 1                 借出的连接数 = 1
  session.close()              借出的连接数 = 0
  又执行 select 1(请求结束前)          借出的连接数 = 1
  请求结束之后(teardown)借出的连接数 = 0
  池事件顺序(checkout / checkin): ['checkout', 'checkin', 'checkout', 'checkin']

上一篇:数据库连接池;下一篇:expire_on_commit。

连接泄漏

实测 · 本机执行输出
  连续 4 次请求 /leak 的状态码: [200, 200, 500, 500] | 借出的连接数: 2
  用 with 管理的 /noleak 连续 6 次: [200, 200, 200, 200, 200, 200] | 借出的连接数: 0

慢操作与连接

实测 · 本机执行输出
  /hold     3 个并发请求:各自耗时 [1188, 1188, 2194] ms,总耗时 2194 ms,状态码 [200]
  /release  3 个并发请求:各自耗时 [1180, 1180, 1182] ms,总耗时 1182 ms,状态码 [200]

每个请求一个线程(gthread);池是 2 个连接,超时 3 秒。

流式响应

实测 · 本机执行输出
  视图里先查询,再返回生成器:流式响应进行中另一个请求看到的借出连接数 = 0,流结束后 = 0
  生成器里(流式过程中)才查询:流式响应进行中另一个请求看到的借出连接数 = 1,流结束后 = 0

分步演示

Pool Checkout 与连接释放
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。Flask-SQLAlchemy 3.1.1、psycopg 3.3.6、自己起的 PostgreSQL(非默认端口)、gunicorn gthread
    • 没有验证:异步视图与 asyncio 引擎下的连接占用、Celery 任务里的连接管理、泄漏的连接被垃圾回收时的行为(本篇只观察到请求结束后借出数停在 2,没有查原因)、连接泄漏的监控手段
    京ICP备2026067502号-1京公网安备11010502063487号