22.5 连接预算与过载边界
连接治理的目标不是让所有请求最终都能排到数据库,而是:
正常负载低延迟通过
短 burst 在便宜位置有限排队
持续过载尽早拒绝或降级
高价值与控制流保留能力
取消与 deadline 向下传播
恢复时不产生重试洪峰这需要同时预算 connection、active transaction、queue length、等待时间和
重试。只设置一个 max_connections 没有形成过载边界。
22.5.1 按服务分配连接、并发与队列
先分服务,再分数字
一个数据库集群常同时承担:
critical OLTP writes
interactive reads
background jobs
reporting/ETL
migration/admin
monitoring/backup/control它们的价值、服务时间和失败策略不同。共享一个 pool 意味着:
报表占满 backend
-> 下单请求排队
-> health/readiness 也超时
-> orchestration 重启更多实例
-> reconnect storm按服务分配至少包括:
| 维度 | 问题 |
|---|---|
| client connections | 应用能保持多少逻辑连接 |
| server connections | 最多占多少 PostgreSQL backend |
| active concurrency | 同时执行多少事务/查询 |
| queue length/time | 多久后拒绝,最多积压多少 |
| resource | CPU、I/O、work_mem、temp、lock |
| priority | 过载时谁先被削减 |
| reserve | 谁能在拥塞时进入控制面 |
connection budget 不是 concurrency budget
transaction pool 可以有:
1000 client connections
40 server connections
20 typical active transactions三者都合理,只要:
- client process/FD/TLS 成本可承受;
- server pool 不超过数据库预算;
- active workload 经压测;
- 960 个等待者不会无限停留;
- deadline 和拒绝策略有效。
把所有实例相加
预算必须按全局 deployment:
blue version
green version
autoscaling maximum
cron workers
manual jobs
disaster-recovery warm instance例如:
normal 20 pods × pool 8 = 160 clients
deploy overlap another 20 = 160
autoscale headroom 10 = 80
workers 8 × pool 4 = 32
total potential = 432若 PgBouncer server pool 是 32,客户端能排队;若直连,则一次 blue/green 发布就可能把 backend 翻倍。
database/user pair 的隔离
PgBouncer pool key 通常包含 database 与 user。可以用:
pg36_shop_app
pg36_shop_worker
pg36_shop_report
pg36_shop_migrate建立不同预算与权限。
不要无界创建 role:
100 tenants × default_pool_size 20即使每个 pair 很小,乘法也可能超过 PostgreSQL。可通过:
max_db_connections;max_user_connections;- per-database/user override;
- group role + application context;
- tenant 分片;
- idle pool cleanup;
控制。
负载并发门槛
连接池限制的是 backend 数,不直接限制一条连接里 query 的资源。还需要:
- application concurrency semaphore;
- job worker count;
- query governor;
- statement timeout;
- role/database resource policy;
- workload isolation/offline replica。
例如 8 条并行 hash join 可能比 32 条短索引查询更重。server pool size 要按 workload mix 压测。
queue 的接受条件
定义:
Q_max 最大等待者
W_max 最大等待时间
λ 到达率
μ 每 server slot 服务率
c server slots长期稳定至少要求:
[ \lambda < c\mu ]
否则任何有限 queue 最终都会满。
短 burst 的近似吸收能力:
[ Q_{\text{needed}} \approx \int_{\text{burst}} \max(0,\lambda(t)-c\mu(t))dt ]
这不是精确 queueing model,但迫使团队问“burst 多大、持续多久”,而不是把 max queue 随手设成 10,000。
Pigsty/HAProxy/PgBouncer 三处上限
本章渲染配置含:
HAProxy service maxconn 5000
HAProxy per backend maxconn 3000
HAProxy per backend maxqueue 128
PgBouncer max_client_conn 20000
PgBouncer default_pool_size 50
PgBouncer reserve_pool_size 30
PgBouncer max_db_connections 100
PgBouncer max_user_connections 100
PostgreSQL max_connections 500大数字不表示应使用到它。真正的有效边界是这些限制、活跃 pair 和所有实例 总和的组合。
如果 HAProxy 允许 5000、PgBouncer 允许 20000,而应用 deadline 2 秒, 仍应在更早层用 application concurrency/query wait timeout 拒绝过期工作。
一份分配账本
cluster_backend_ceiling: 500
reserved:
superuser: 10
platform_admin_monitor: 40
incident_control: 20
maintenance_replication: 30
allocatable_workload: 400
services:
shop_rw:
server_pool: 40
reserve: 8
active_target: 24
queue_timeout: 200ms
shop_ro:
server_pool_per_replica: 20
active_target: 12
queue_timeout: 300ms
report:
server_pool: 4
active_target: 2
statement_timeout: 10min
migration:
direct_connections: 2
headroom:
unallocated: 100headroom 不是浪费,而是吸收估算误差、maintenance 和 incident action。
22.5.2 超时层级、取消传播与熔断
一次请求经过多个时钟
user deadline
HTTP/RPC deadline
application pool acquire timeout
DNS/connect/TLS timeout
HAProxy queue/connect timeout
PgBouncer query_wait_timeout
PostgreSQL lock_timeout
PostgreSQL statement_timeout
idle_in_transaction_session_timeout
client read/write socket timeout若各自独立设置,会出现:
上游 2 秒取消
下游 pool 还等 120 秒
SQL 继续跑 10 分钟
失败后 middleware 再重试资源在用户离开后继续消耗。
deadline budget
把请求总预算 (D) 分解:
[ D = T_{\text{acquire}} +T_{\text{connect}} +T_{\text{queue}} +T_{\text{lock}} +T_{\text{execute}} +T_{\text{return}} +T_{\text{margin}} ]
各层不一定串行,有些重叠;这个式子是设计账本,不是精确 profiler。
例如 2 秒交互请求:
app acquire 150 ms
connect/role check 300 ms
database statement 1200 ms
return/margin 350 msPgBouncer query_wait_timeout=120s 显然不匹配该请求。可以按服务 override,
或让应用 acquire/overall deadline 更早终止并正确 cancel。
connect timeout 不是 failover timeout
多 host 串行尝试时:
[ T_{\text{connect worst}} \approx \sum_{\text{host}} T_{\text{connect host}}
- DNS/TLS/backoff ]
三个 host × 5 秒可能已经超过 request deadline。驱动是否并行尝试、每 host 还是全局 timeout,要按版本确认。
statement_timeout
PostgreSQL 从收到命令开始计时;达到后取消当前 statement。它不一定包含:
- 应用 pool 等待;
- PgBouncer queue;
- DNS/TCP/TLS;
- 客户端处理结果;
- 上一次 idle transaction。
按 role/database 设置比一个全局值更实用:
ALTER ROLE pg36_shop_app IN DATABASE pg36_shop
SET statement_timeout = '3s';
ALTER ROLE pg36_shop_report IN DATABASE pg36_shop
SET statement_timeout = '10min';事务级可以:
BEGIN;
SET LOCAL statement_timeout = '800ms';
...
COMMIT;lock_timeout
它只限制等待 lock 的时间,不限制 query 总执行时间。
DDL/migration 常用:
SET lock_timeout = '1s';
SET statement_timeout = '30min';意图是:
拿不到锁快速失败,不在生产长队列中等待;
拿到锁后允许受控操作运行。不要把 lock timeout 设置得比 statement timeout 更长而期待它生效。
idle transaction timeout
idle_in_transaction_session_timeout切断已开始事务但长期不发 query 的 session。它是 vacuum/lock 保护,不应拿来 清理普通 idle pool connection。
还有普通 idle_session_timeout,但连接池可能把 idle connection 视为资产;
使用前要评估 reconnect churn 和 middleware 兼容。
取消传播
客户端取消 PostgreSQL query 通常需要单独 cancel request/connection path。 经过 pool/proxy 时要验证:
- cancel 能定位正确 backend;
- client 已换 backend 后不会 cancel 别人;
- proxy 是否转发;
- timeout 后 transaction 是否处于 aborted;
- connection 是否应丢弃;
- server query 是否确实停止。
不能只看到 HTTP 499/timeout 就认为数据库工作结束。
熔断器
breaker 保护的是下游与自身:
closed 正常请求
open 快速拒绝,停止放大故障
half-open 少量探针判断恢复触发信号应比“任意 SQL error”精细:
- pool acquire timeout;
- connection/role check failure;
- sustained queue;
- downstream saturation;
- known infrastructure outage。
不要因业务约束错误、syntax error 或唯一冲突打开数据库 breaker。
retry budget
如果原始请求率为 (\lambda),平均重试 (r) 次:
[ \lambda_{\text{downstream}} = \lambda(1+r) ]
故障时 r 往往上升,正好放大最脆弱的下游。为服务定义:
maximum retries per request
maximum retry traffic percentage
backoff+jitter
non-retryable SQLSTATE
outcome-unknown reconciliationload shedding
在数据库彻底饱和前:
- 拒绝可选报表/推荐;
- 降低 background worker;
- 使用缓存/较旧副本;
- 限制 expensive endpoint;
- 保留写入/控制面;
- 必要时进入只读或功能降级。
load shedding 是业务决策,不应完全交给随机 connection timeout。
22.5.3 为 ch34 的止血动作预留控制点
第 34 章会处理连接风暴、CPU、内存、磁盘和 I/O 过载。本章要提前提供可用 控制点,否则事故中只能粗暴重启。
控制点一:客户端并发
feature flag
rate limiter
per-tenant quota
worker concurrency
queue consumer pause
autoscaler maximum
retry switch优点:最接近业务价值,能在请求进入数据库前止血。
控制点二:应用 pool
max size
min idle
acquire timeout
connection lifetime
idle timeout
warmup rate
validation动态缩 pool 可能只影响新借用,不能立即终止 in-flight transaction。变更行为 按具体 driver 验证。
控制点三:PgBouncer
管理 console 可观察/控制:
SHOW POOLS / STATS / CLIENTS / SERVERS
PAUSE / RESUME
DISABLE / ENABLE
RECONNECT
RELOAD
SET changeable_setting
KILL database这些动作风险不同:
PAUSE等待 server connection 释放,可用于维护;RECONNECT database刷新 server connection;KILL更具破坏性;- runtime
SET会形成声明漂移; - global action blast radius 可能跨服务。
必须有 exact database/instance/role guard 和复位证据。
控制点四:HAProxy
disable/enable server
backend weight
maxconn/maxqueue
drain
health threshold
service removal把流量移到副本/其他主库前,先确认目标容量与一致性。转移过载往往只是移动 事故。
控制点五:PostgreSQL role/database
ALTER ROLE ... CONNECTION LIMIT ...;
ALTER DATABASE ... ALLOW_CONNECTIONS false;
ALTER ROLE ... SET statement_timeout ...;
ALTER ROLE ... SET work_mem ...;
REVOKE CONNECT ...;
SELECT pg_cancel_backend(...);
SELECT pg_terminate_backend(...);其中 revoke/terminate 可能影响业务或锁,属于受控事故动作。不要把示例 SQL 做成无 guard 的一键脚本。
控制点六:保留管理路径
过载时最怕 DBA 也连不进去。预留:
- superuser/reserved connection;
- 独立 admin role;
- direct endpoint;
- source network allowlist;
- 小而独立的 admin pool;
- break-glass credential;
- out-of-band host access。
监控 exporter 也不应完全依赖已满的普通业务池。
每个控制点都要有回滚
事故动作表:
| 动作 | 目的 | 成功证据 | 副作用 | 复位 |
|---|---|---|---|---|
| 降 worker | 减少 arrival | queue/DB active 降 | backlog 增 | 分阶段恢复 |
| 缩 app pool | 限制 client | acquire wait 可控 | request reject | 恢复声明 |
| pool PAUSE | 维护/切换 | server released | client wait | RESUME |
| RECONNECT db | 刷新 backend | role/path probe 通过 | login burst | 无持久配置 |
| HAProxy drain | 摘除 backend | sessions 收敛 | 容量下降 | enable/weight |
| cancel query | 释放资源 | query 消失 | 事务 abort | 应用重试 |
| terminate | 强制止血 | backend 结束 | outcome unknown | reconcile |
“止血成功”不等于“事故解决”。必须保留证据、找根因和复位。
最小过载仪表盘
应用:
request rate/error/latency
pool waiters/acquire timeout
retry rate
in-flight by endpointPgBouncer:
cl_active/cl_waiting
sv_active/sv_idle
maxwait
login/errorPostgreSQL:
active/idle in transaction
wait events/locks
CPU/run queue
I/O latency
temp bytes
WAL/replication lag代理:
backend status/backup use
current sessions/queue
connect errors端到端 correlation 必须有 service、database、user、application_name 和时间。
本节检查表
[ ] service 按价值、服务时间和失败策略分预算
[ ] client/server/active/queue 四种上限分开
[ ] blue-green/autoscale/worker 乘法已计入
[ ] sustained overload 有拒绝而非无限排队
[ ] timeout 形成一份 deadline budget
[ ] cancel 到 PostgreSQL backend 已实测
[ ] retry 有 SQLSTATE 分类、budget 和 jitter
[ ] optional workload 能独立 shed
[ ] admin/monitor 有保留连接与直连路径
[ ] PgBouncer/HAProxy/PostgreSQL 控制动作有 guard 与复位
[ ] ch34 可以复用这些控制点,而不是事故时发明参考资料
- PostgreSQL 18:Client Connection Defaults
- PostgreSQL 18:Server Configuration
- PgBouncer:Configuration
- PgBouncer:Administration console
上一节:路由与故障切换 · 返回本章目录 · 下一节:Pigsty 服务接入层 · 查看全书目录 · 查看索引中心