bigywater.tech
Flask · 91

数据库连接池

连接池默认是多大?耗尽时会怎样?连接被服务端断掉之后会不会出错?多个 gunicorn worker 会把数据库连接数撑到多少?这篇用真实的 PostgreSQL 逐项验证。

5 + 10
QueuePool 默认的 pool_size 与 max_overflow
15 个
3 个 gunicorn worker、每池 3 + 2 时 PG 上同时出现的最大连接数
11 倍
NullPool 相对 QueuePool 的单次借连接耗时(本机回环)

默认配置

实测 · 本机执行输出
  类型: QueuePool | pool_size = 5 | max_overflow = 10 | timeout = 30.0 | recycle = -1 | pre_ping = False
  刚创建: Pool size: 5  Connections in pool: 0 Current Overflow: -5 Current Checked out connections: 0 | PG 连接数 0
  借出 2 个连接: Pool size: 5  Connections in pool: 0 Current Overflow: -3 Current Checked out connections: 2 | PG 连接数 2
  归还 1 个: Pool size: 5  Connections in pool: 1 Current Overflow: -3 Current Checked out connections: 1 | PG 连接数 2
  engine.dispose() 之后 PG 连接数 0

上一篇:Rollback 的实际行为;下一篇:Pool Checkout 与连接释放。

连接池耗尽

实测 · 本机执行输出
  借 3 个(2 + 1 溢出): Pool size: 2  Connections in pool: 0 Current Overflow: 1 Current Checked out connections: 3 | PG 连接数 3
  借第 4 个 → 等了 1010 ms 后 TimeoutError: QueuePool limit of size 2 overflow 1 reached, connection timed out, timeout 1.00 (Background on
  归还 1 个连接后 PG 连接数: 3 (池里还有空位,这个连接被留在池里,没有关闭)
  再归还 1 个后: 3 | 池状态: Pool size: 2  Connections in pool: 2 Current Overflow: 1 Current Checked out connections: 1
  三个都归还后:2(池只保留 pool_size=2 个,多出的那个溢出连接被真正关闭) | 池状态: Pool size: 2  Connections in pool: 2 Current Overflow: 0 Current Checked out connections: 0

连接被断开与 pre_ping

实测 · 本机执行输出
  默认(无 pre_ping)       服务端杀掉空闲连接后下一次查询 → OperationalError: terminating connection due to administrator command
    (没有 pre_ping 时,失败那次之后坏连接被丢弃,再查一次 → 成功)
  pool_pre_ping=True   服务端杀掉空闲连接后下一次查询 → 成功(池发现连接坏了,换了一个新的)

第 100 篇会专门讲断线后的恢复。

池化的收益

实测 · 本机执行输出
  QueuePool  中位数 0.17 ms
  NullPool   中位数 1.82 ms

多个 worker

实测 · 本机执行输出
  1 个 worker × 16 线程,40 个并发请求(每个查询 0.5 秒):PG 上最多同时有 5 个连接(每个池上限 3 + 2 = 5,1 个池最多 5),总耗时 4.1 秒,状态码 [200]
  3 个 worker × 16 线程,40 个并发请求(每个查询 0.5 秒):PG 上最多同时有 15 个连接(每个池上限 3 + 2 = 5,3 个池最多 15),总耗时 1.5 秒,状态码 [200]

这一节用 gunicorn 的 gthread worker,每个 worker 进程里一个独立的 Engine。

分步演示

数据库连接池
本步的对象

    本文用到的工具与边界

    • 本机 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(非默认端口)
    • 没有验证:pool_recycle 在 MySQL 的 wait_timeout 下的作用、真实网络与 TLS 下新建连接的耗时、PgBouncer 等外部连接池、预加载应用(--preload)后在 fork 里共享 Engine 的问题、pool_use_lifo 与 pool_reset_on_return 的取值
    京ICP备2026067502号-1京公网安备11010502063487号