深色模式
数据库连接数容量
应用实例数 × 池大小 = 总连接数。这个乘法常常在扩容应用后被忽略,最终把数据库压垮。本文给出可计算的连接数容量模型。
适用环境
- MySQL / PostgreSQL 及任意使用连接池的应用(HikariCP、Druid、pgbouncer 等)
- 能登录数据库执行
SHOW STATUS/pg_stat_activity - 应用侧能看到连接池监控指标
bash
# 取应用实例数(K8s 示例)
kubectl get deploy order-api -o jsonpath='{.spec.replicas}'1
2
2
操作步骤
1. 查数据库当前连接数与上限
sql
-- MySQL
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';1
2
3
4
2
3
4
sql
-- PostgreSQL
SHOW max_connections;
SELECT count(*), state FROM pg_stat_activity GROUP BY state;1
2
3
2
3
promql
# Prometheus 采集(mysqld_exporter / postgres_exporter)
mysql_global_variables_max_connections
mysql_global_status_threads_connected1
2
3
2
3
2. 统计连接来源分布
sql
-- MySQL:按用户与来源 IP 分组
SELECT user, substring_index(host, ':', 1) AS ip, count(*)
FROM information_schema.processlist GROUP BY user, ip ORDER BY 3 DESC LIMIT 10;1
2
3
2
3
sql
-- PostgreSQL
SELECT usename, application_name, client_addr, count(*)
FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC LIMIT 10;1
2
3
2
3
3. 建立连接数模型
text
总连接数 = 应用实例数 × 每实例池上限
+ 管理连接(备份/监控/DBA)× 余量
示例:
应用实例 8 个,HikariCP maximumPoolSize=20
总连接 = 8 × 20 = 160
加上备份与运维 20 条 → 180
max_connections 建议 ≥ 180 / 0.7 ≈ 257 → 设 3001
2
3
4
5
6
7
8
2
3
4
5
6
7
8
4. 计算合理的池大小(不是越大越好)
数据库连接是串行资源,连接过多只会增加上下文切换与锁等待。经验公式:
text
池大小 ≈ CPU核数 × 2 + 磁盘数 (PostgreSQL/HikariCP 常用经验)
或
池大小 = 峰值并发请求 × 单请求平均DB耗时(s) / 实例数
示例:峰值 3000 QPS,单请求 DB 耗时 5 ms,8 个实例
池大小 = 3000 × 0.005 / 8 ≈ 1.9 → 配 4~8 即可,20 是浪费1
2
3
4
5
6
2
3
4
5
6
先用公式算下限,再用压测验证:逐步调大池,直到吞吐不再增长即为合适值。
5. 配置示例
yaml
# HikariCP(Spring Boot)
spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 3000 # 拿不到连接最多等 3 秒,快速失败
validation-timeout: 1000
leak-detection-threshold: 300001
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
ini
# pgbouncer(连接复用,适合实例多的场景)
pool_mode = transaction
max_client_conn = 2000
default_pool_size = 20
max_db_connections = 1001
2
3
4
5
2
3
4
5
实例数很多时,用 pgbouncer / ProxySQL 做连接收敛,比无限调大 max_connections 更安全。
6. 预留 DBA 专用连接
sql
-- MySQL 8.0 提供管理员专用连接,普通连接打满时仍可登录
SHOW VARIABLES LIKE 'admin_address';1
2
2
PostgreSQL 保留 superuser_reserved_connections,务必不要设为 0。
验证
sql
-- 峰值时段的水位应 ≤ 70%
-- MySQL
SELECT @@max_connections AS max_c,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Threads_connected') AS used_c;1
2
3
4
2
3
4
promql
# 连接池等待时间的 P99,持续 > 0 说明池太小
histogram_quantile(0.99, rate(hikaricp_connection_usage_seconds_bucket[5m]))1
2
2
压测时同步观察:连接获取等待时间、DB 侧 Threads_running、慢查询数量。
常见坑
扩容应用不重算连接数
应用从 4 台扩到 20 台,池大小不变,总连接翻 5 倍直接打满 max_connections,表现为全站 5xx。扩容应用前必须先算 DB 侧承受力。
连接池越大越好
超过 CPU 承载的连接数会让 DB 吞吐下降、锁等待上升。用压测找饱和点,而不是把池调到 200。
没配 connection-timeout
默认无限等待会导致请求堆积、线程池耗尽。必须设置秒级超时并用连接池指标告警。
长事务占用连接
一个慢事务长时间占着连接,峰值时连接被吃光。要监控 information_schema.innodb_trx 中的长事务并设置 innodb_lock_wait_timeout。