应通过监控连接使用率趋势而非瞬时值来实时预警:PG用pg_stat_activity计数与max_connections比值≥85%持续2分钟告警;MySQL结合Threads_connected、Threads_running及滑动窗口分析,避免误判业务波动。

如何实时发现 max_connections 即将耗尽
PostgreSQL 和 MySQL 的连接数被打满时,新连接会直接拒绝,错误通常是 FATAL: remaining connection slots are reserved for non-replication superuser connections(PG)或 Too many connections(MySQL)。不能等报错才响应,得提前预警。
关键不是查当前用了多少,而是看「趋势」+「临界阈值」。建议在监控系统中配置:
- PostgreSQL:每 30 秒采集
SELECT COUNT(*) FROM pg_stat_activity;,对比SHOW max_connections;,当使用率 ≥ 85% 持续 2 分钟触发告警 - MySQL:用
SHOW STATUS LIKE 'Threads_connected';和SHOW VARIABLES LIKE 'max_connections';,注意Threads_connected包含空闲连接,需结合Threads_running判断真实压力 - 避免只看瞬时值——短时 spike 可能是正常业务波动,要叠加滑动窗口(如 5 分钟内最大值 / 平均值)
临时扩容 max_connections 的安全操作路径
动态调大 max_connections 不是简单改个参数重启就行,它受底层资源硬约束。不检查就扩,可能引发 OOM 或内核拒绝分配内存。
执行前必须确认:
- PostgreSQL:检查
shared_buffers和work_mem是否已随连接数线性增长——每个连接默认至少占用几 MB 内存,max_connections从 100 扩到 500,若work_mem=4MB,理论内存新增约(500−100)×4MB = 1.6GB - MySQL:确认
table_open_cache、innodb_buffer_pool_instances等关联参数是否需同步调整,否则可能卡在文件描述符或 mutex 竞争上 -
操作系统层:用
ulimit -n查当前进程允许打开的文件数,max_connections必须 ≤ 该值 × 0.8(留出日志、socket 等其他句柄空间)
线上临时扩容推荐分两步走:ALTER SYSTEM SET max_connections = 300; SELECT pg_reload_conf();(PG),或修改 my.cnf 后 mysqladmin shutdown && mysqld_safe &(MySQL,无法在线生效)。
为什么连接数容易被“假占满”
真正活跃的连接可能只有十几个,但 pg_stat_activity 显示几百个——这通常不是配置太小,而是应用没正确释放连接。
常见诱因:
- 应用端未调用
connection.close()或未进finally块,尤其在异常分支里漏关 - 连接池配置不合理:比如 HikariCP 的
maximumPoolSize设为 100,但connection-timeout过长(如 30s),导致请求堆积后大量连接卡在“获取中”状态 - 数据库端存在长事务或锁等待:
state = 'idle in transaction'或wait_event = 'Lock'的连接会持续占 slot,需用pg_blocking_pids(pid)定位源头
这类问题扩 max_connections 只是掩盖症状,必须配合 pg_stat_activity 中的 backend_start、state_change、query_start 字段做时间差分析。
告警后快速止损的应急命令清单
告警响了,第一反应不是改配置,而是先释放可杀的连接保服务。
PostgreSQL(谨慎执行):
SELECT pid, usename, application_name, state, now() - backend_start AS uptime, now() - state_change AS idle_time, query
FROM pg_stat_activity
WHERE state IN ('idle in transaction', 'idle') AND now() - state_change > interval '5 minutes';确认无误后批量终止:
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - state_change > interval '5 minutes';
MySQL 类似:
KILL [CONNECTION] [thread_id]; -- 先查 SHOW PROCESSLIST; 找出 Command='Sleep' 且 Time > 300 的
注意:pg_terminate_backend() 对超级用户连接无效;MySQL 的 KILL 不会回滚事务,只断开连接,后续需人工检查数据一致性。
真正难的不是扩容动作本身,而是区分“该扩”还是“该修”——连上去看到 200 个 idle 连接,90% 的情况该翻应用日志,而不是去改 max_connections 配置文件。










