MySQL连接数打满排查与治理实战指南

作者:技术拾光

时间:2026-08-14

浏览:73

一、现象:不是慢,是直接连不进生产环境最常见的两个信号:应用日志批量刷 Too many connections,监控里 Threads_connected 顶到 max_connections 上限;与此同时新请求开始超时,老请求还在跑,但新连接一律被拒。这个状态本身不复杂,难在"为什么突然打满"

一、现象:不是慢,是直接连不进

MySQL连接数打满排查与治理实战指南

生产环境最常见的两个信号:应用日志批量刷 Too many connections,监控里 Threads_connected 顶到 max_connections 上限;与此同时新请求开始超时,老请求还在跑,但新连接一律被拒。

这个状态本身不复杂,难在"为什么突然打满"——绝大多数不是真的流量暴涨,而是连接没被正常回收,雪球越滚越大。下面按"确认→定位→根因→治理"的顺序拆解。

二、先确认是不是真的打满了

不要只看报错,先读数确认当前水平:

# 当前最大连接数与已用连接数
mysql -e "SHOW VARIABLES LIKE 'max_connections'; SHOW STATUS LIKE 'Threads_connected'; SHOW STATUS LIKE 'Threads_running';"

# 改成全局状态一次性看全
mysql -e "SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected','Threads_running','Max_used_connections','Aborted_connects','Connections');"

关键看三个数:

  • Threads_connected:当前活着的总连接数,是否等于或逼近 max_connections
  • Threads_running:真正在执行的线程,通常远小于 connected,如果它也接近上限,说明不是空闲连接堆积,而是真有海量并发 SQL
  • Max_used_connections:历史峰值,对照 max_connections 看是否曾经顶满过
状态变量含义异常信号Threads_connected当前总连接数逼近 max_connectionsThreads_running正在执行的线程接近 connected,说明并发 SQL 真高Max_used_connections历史峰值多次触及上限,需调大或治本Aborted_connects失败连接累计持续增长,客户端配置或网络有问题Connections累计总连接数配合时间看每秒新建速率

三、连接都耗在哪:按来源聚合

确认打满之后,下一步是看这些连接来自哪个应用、哪台机、卡在哪个库:

# 按客户端 IP 聚合连接数(在 mysql 里执行)
mysql -e "SELECT SUBSTRING_INDEX(host,':',1) AS client, COUNT(*) AS conn FROM information_schema.processlist GROUP BY client ORDER BY conn DESC LIMIT 20;"

# 按 user 聚合
mysql -e "SELECT user, COUNT(*) AS conn FROM information_schema.processlist GROUP BY user ORDER BY conn DESC;"

# 看卡住的语句:执行时间超过 5 秒的连接
mysql -e "SELECT id, host, user, db, time, state, LEFT(info,80) AS sql_text FROM information_schema.processlist WHERE command<>'Sleep' AND time>5 ORDER BY time DESC;"

processlist 里重点盯两类:

  • command='Sleep' 且 time 很大的:这是已经空闲但没被客户端释放的连接,往往占掉大半配额
  • state 卡在 Sending data / Locked / Waiting for table flush 且 time 长:慢查询或锁等待占着连接不撒手

四、四类根因,按出现频率排序

  1. 连接池没设上限或设太大。应用侧连接池 max 配得比数据库 max_connections 还大,N 个实例一扩,总连接直接翻倍超 DB 上限。
  2. wait_timeout / interactive_timeout 过长。默认 28800 秒(8 小时),客户端异常退出时 DB 侧连接要等 8 小时才回收,空闲连接长期占坑。
  3. 慢查询 / 锁等待。一条全表扫或行锁未提交的事务,把连接钉住几十秒,并发一来连接池迅速被耗光。
  4. 应用拿了连接不关。代码里 try 没 finally、异常分支漏了 close,连接泄漏,重启前只增不减。
    五、治理:参数 + 连接池两头收

参数侧先看当前超时与线程缓存:

mysql -e "SHOW VARIABLES LIKE 'wait_timeout'; SHOW VARIABLES LIKE 'interactive_timeout'; SHOW VARIABLES LIKE 'thread_cache_size'; SHOW VARIABLES LIKE 'max_connections';"
参数默认中高并发建议说明max_connections151按实例数×池上限留余量不是越大越好,受内存限制wait_timeout28800300~600空闲非交互连接回收时间(秒)interactive_timeout28800300~600交互连接回收时间thread_cache_size8(或 0)如未读到默认则 16~32复用线程,降新建开销

在线改(重启失效,用于应急):

mysql -e "SET GLOBAL wait_timeout=600; SET GLOBAL interactive_timeout=600; SET GLOBAL max_connections=800;"

持久化写配置文件 /etc/my.cnf 的 [mysqld] 段:

[mysqld]
max_connections = 800
wait_timeout = 600
interactive_timeout = 600
thread_cache_size = 32

应用侧连接池必须兜底,不能只靠 DB 调大。Python(SQLAlchemy)示例:

from sqlalchemy import create_engine

engine = create_engine(
"mysql+pymysql://user:pass@db-host:3306/app",
pool_size=20, # 常驻连接上限
max_overflow=10, # 超出 pool_size 的临时连接
pool_recycle=280, # 小于 wait_timeout,提前回收防失效连接
pool_pre_ping=True, # 取出连接前探活,避免拿到已断开的连接
pool_timeout=30, # 拿不到连接等待上限,超时即抛错而非无限等
)

Go(database/sql)示例:

db, _ := sql.Open("mysql", "user:pass@tcp(db-host:3306)/app")
db.SetMaxOpenConns(30) // 最大打开连接数
db.SetMaxIdleConns(10) // 最大空闲连接数
db.SetConnMaxLifetime(280 * time.Second) // 连接最长存活,小于 wait_timeout
db.SetConnMaxIdleTime(60 * time.Second) // 空闲回收时间

六、连接稳定与出口的关系

应用服务器连数据库、再连下游第三方接口时,通常都会依赖长连接。可要是底层走的是那种会自动切换的共享线路,问题就来了:一旦出口 IP 发生变化,对端往往会把它当成一次全新的会话,于是连接被反复重建。表面看只是线路切换,实际上却会悄悄把连接消耗放大。针对这类长连接,常见的出口方案无非几种:直接使用云厂商分配的固定公网地址、走企业专线,或者选择按 IP 独立分配的服务商(如 LinkStatic 这类固定 ISP 专线)。说到底,关键就一条——让出口地址长期保持稳定,再配合前面提到的池化和超时治理,整体连接水平才会更容易控制。

七、长效机制:把阈值监控起来

治完要防止复发,最关键的是把连接数纳入监控并设告警:

# 每分钟采样,超过 max_connections 的 80% 就告警
mysql -N -e "SHOW STATUS LIKE 'Threads_connected';" | awk '{c=$2} END{
m=system("mysql -N -e "SHOW VARIABLES LIKE '"'"'max_connections'"'"'" | awk "{print \$2}"");
if (c > m*0.8) print "WARN threads_connected="c" max="m
}'

再配合 Grafana 把 Threads_connected / max_connections 这条曲线盯起来,通常只要斜率开始异常上冲,往往会比 Too many connections 报错更早发出信号,也就能提前抢出一段处理窗口。与此同时,把慢查询日志一并叠加进日常巡检里,比如开启 slow_query_log=1、设置 long_query_time=1,这类问题大多都能在真正演变成故障前就被拦下来。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›