线上高频事故:MySQL 突然报 Too many connections,服务雪崩、接口全挂。本文从原因定位、实时排查到永久根治,一套方案搞定所有场景。
一、前言
Too many connections 是 MySQL 最常见的线上故障之一。一旦出现,整个业务几乎不可用:
- 应用无法连接数据库
- 接口大量超时
- 监控告警刷屏
- 老连接不释放,新连接进不来
很多人只会临时 max_connections 改大,但治标不治本,过几天又炸。
本文带你:快速恢复 → 定位根因 → 彻底根治,生产 & 面试都能用。
二、先看懂三个关键指标
sql
-- 最大连接数上限
show variables like 'max_connections';
-- 当前正在使用的连接数
show status like 'Threads_connected';
-- 历史峰值
show status like 'Max_used_connections';
只要 Threads_connected 接近 max_connections,业务就开始报错:
Too many connections
三、连接数满的【超全原因汇总】
1. 应用代码层面原因(最常见)
- 连接泄漏
- 申请连接后未关闭
- try-catch 异常路径未释放
- ORM 误用、嵌套事务未释放
- 长事务 / 大事务占住连接不释放
- 事务里调用 HTTP/RPC/ 第三方接口
- 事务里循环处理大量数据
- 事务未提交 / 回滚,连接一直持有
- 短连接风暴
- 每次请求都新建连接
- 脚本语言(PHP/Python)无连接池
- 高并发下疯狂创建 / 销毁连接
- 连接池配置不合理
- max-active 设置过大
- 多个微服务连接数总和打爆数据库
- 连接池等待队列过长,导致连接堆积
- 应用重启不彻底
- 旧进程未杀死,继续持有连接
- 多实例部署重复占用连接
2. SQL 与索引问题导致连接堆积
- 大量慢查询阻塞连接
- 未加索引、全表扫描
- 大表 JOIN、子查询嵌套
- 文件排序、临时表导致 SQL 执行极慢
- 锁等待严重
- 行锁冲突、表锁、间隙锁
- 热点行更新导致大量事务阻塞
- 连接占着不释放,新连接进不来
- 统计信息过旧导致执行计划走错
- 查询瞬间变慢,连接暴增
- 大量统计 / 报表类查询压垮数据库
- 后台任务、定时任务集中执行
- 占用大量连接长时间不释放
3. MySQL 自身配置与运行问题
- max_connections 默认太小(默认仅 151)
- 文件描述符不足(系统级限制)
- Linux: open files 限制太小
- 导致 MySQL 无法新建连接
- DNS 反向解析卡顿
- MySQL 每次连接反查主机名
- 网络慢 → 连接建立极慢 → 堆积
- 线程缓存不足
- thread_cache_size 过小
- 频繁创建销毁线程,性能下降,连接堆积
- 空闲连接超时设置过长
- wait_timeout /interactive_timeout 太大
- sleep 连接长期不释放,占满连接数
- MySQL 负载过高
- CPU 100%、IO 高、磁盘满
- 执行缓慢,连接无法快速释放
- binlog 刷盘、redo 日志竞争
- 高写入下磁盘瓶颈,事务提交慢
- 内存不足 OOM 前兆
- 内存占满,性能急剧下降
- 连接处理不过来
4. 架构与流量问题
- 流量突增 / 活动压测
- 秒杀、促销、爬虫突增
- 分布式架构连接数叠加
- 几十个微服务连同一个 MySQL
- 单个服务合理,总和爆炸
- 读写分离失效
- 所有读都走主库
- 主库连接瞬间打满
- 网关 / 代理重试风暴
- 超时重试、失败重试
- 指数级放大请求,连接数翻倍增长
- 监控、备份、巡检工具占用连接
- Prometheus、Zabbix、监控脚本
- 每秒连接一次,累积大量连接
5. 网络与环境问题
- 网络抖动 / 延迟高
- 应用与 MySQL 之间网络不稳定
- 连接建立慢、释放慢
- TCP 参数不合理
- TIME_WAIT 过多
- 端口耗尽、握手缓慢
- 虚拟机 / 容器资源限制
- Docker/K8s CPU / 内存限制
- MySQL 性能下降,连接堆积
- 云数据库底层性能波动
- 磁盘性能突降、实例拥挤
6. 恶意攻击与异常行为
- CC 攻击、恶意刷接口
- 漏洞扫描、暴力破解数据库
- 死锁循环重试
- 业务死锁 → 报错重试 → 更多连接
- 大量未授权连接尝试
四、3 分钟应急恢复方案
1. 优先登录 MySQL
普通方式连不上时用 socket:
bash
运行
mysql -uroot -p -S /tmp/mysql.sock
2. 临时加大连接数
sql
set global max_connections = 1000;
3. 批量清理异常长连接
sql
select concat('kill ',id,';')
from information_schema.processlist
where command='Sleep' and time>60;
4. 关闭 DNS 解析(立刻缓解)
sql
set global skip_name_resolve = ON;
5. 调整超时时间
sql
set global wait_timeout = 120;
set global interactive_timeout = 120;
五、精准排查:定位谁在占连接
1. 按用户统计
sql
select user,count(*) cnt
from information_schema.processlist
group by user order by cnt desc;
2. 按 IP 统计(定位服务)
sql
select substring_index(host,':',1) ip,count(*) cnt
from information_schema.processlist
group by ip order by cnt desc;
3. 查慢查询
sql
select * from information_schema.processlist
where command='Query' and time>3 order by time desc;
4. 查长 Sleep 连接
sql
select id,user,host,time,info
from information_schema.processlist
where command='Sleep' and time>30;
六、终极解决方案(永久根治)
1. 应用层
- 统一使用连接池(HikariCP/Druid)
- 控制单个服务连接数 15~30
- 禁止长事务,事务只做 DB 操作
- 异常必须释放连接
- 接入限流、熔断、降级
2. MySQL 配置优化(my.cnf)
ini
max_connections = 1000
wait_timeout = 120
interactive_timeout = 120
skip_name_resolve = 1
thread_cache_size = 300
open_files_limit = 65535
3. 架构层
- 读写分离
- 分库分表
- 热点服务独立数据库
- 使用数据库中间件(ShardingSphere/ProxySQL)
4. 治理层
- 慢查询治理
- 索引优化
- 禁止大事务
- 定时任务错峰执行
- 监控连接数、使用率、慢查询
六、总结
Too many connections 本质就三大类:
- 应用没正确释放连接 / 连接池乱配
- SQL 慢、锁等待、事务长导致连接堆积
- 流量过大 + 架构不合理 + 配置过小
急救只能临时扩容,根治必须从应用、SQL、架构、配置四管齐下。
原创不易,点赞 + 收藏 + 关注,后续持续更新 MySQL 线上故障排查实战。