bigywater.tech
Flask · 100

数据库连接断开后的恢复

数据库重启或网络闪断之后,应用要重启吗?会有几个请求失败?pool_pre_ping 和 pool_recycle 各管什么?这篇真的把 PostgreSQL 重启、停机、杀连接,把每种情况的结果量出来。

1 → 0
数据库重启后失败的请求数:默认配置与 pool_pre_ping=True
不用重启应用
数据库恢复之后,连接池自己重新建连接
connection_invalidated
区分断线类错误(可重试)与 SQL 错误的标志

重启之后

实测 · 本机执行输出
  [默认(无 pre_ping)] 重启前池状态: Pool size: 5  Connections in pool: 5 Current Overflow: 0 Current Checked out connections: 0
  [默认(无 pre_ping)] 重启后 8 个串行请求的状态码: [503, 200, 200, 200, 200, 200, 200, 200] | 失败个数 1
  [pool_pre_ping=True] 重启前池状态: Pool size: 5  Connections in pool: 5 Current Overflow: 0 Current Checked out connections: 0
  [pool_pre_ping=True] 重启后 8 个串行请求的状态码: [200, 200, 200, 200, 200, 200, 200, 200] | 失败个数 0

上一篇:SQLite 与 PostgreSQL 行为差异;下一篇:Flask 实现 JSON API。

失败请求的样子

实测 · 本机执行输出
  第一个请求:HTTP 503 {"error":"db unavailable","invalidated":true}

停机与恢复

实测 · 本机执行输出
  数据库停机时:HTTP 503 {"error":"db unavailable","invalidated":false},用时 7 ms
  启动数据库之后,第 1 次请求就恢复了(自 pg_ctl start 返回起 8 ms),无需重启应用

这一节我自己对 pg_ctl 的调用也踩过坑:不带 -o 启动会去用默认端口,所以所有重启命令都显式带了端口与 socket 目录。

请求里重试

实测 · 本机执行输出
  默认配置、数据库刚重启: 200 ok after 2 attempt(s)

执行中被杀

实测 · 本机执行输出
  正在执行的请求: (503, '{"error":"db unavailable","invalidated":true}')
  紧接着的下一个请求: 200

pool_recycle

实测 · 本机执行输出
  没有 pool_recycle    第一次后端进程号 86569,隔 1.3 秒再查 86569 → 还是同一个连接
  pool_recycle=1     第一次后端进程号 86590,隔 1.3 秒再查 86594 → 换了新连接
  (recycle 只在借出连接时按年龄换掉旧连接,不会探测连接是否还活着——那是 pre_ping 的事)

分步演示

数据库断线恢复
本步的对象

    本文用到的工具与边界

    • 本机 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(非默认端口),用 pg_ctl restart、stop、start 与 pg_terminate_backend 制造断线
    • 没有验证:网络层面的假死(丢包而不是连接被拒绝)与 TCP keepalive、读写分离下的主从切换、云数据库的故障转移、断线期间长事务的数据一致性、DNS 变化后的重连
    京ICP备2026067502号-1京公网安备11010502063487号