RDS for MySQL 连接数打满排查实战:从连接统计到参数调优的六步流程
背景
业务高峰期,应用日志突然出现大量 Too many connections,新请求连不上数据库,已建立的连接却还能正常读写。登录 RDS 控制台一看,连接数曲线已经顶到 max_connections 上限。本文记录一次真实的排查过程:先恢复业务,再定位根因,最后把参数和连接池一起调到合理值。
需要先建立一个认知:连接数打满不等于并发真的高,很多时候是连接泄漏、慢查询占着连接不放,或者连接池配得比数据库上限还大。直接调大参数而不找根因,下次高峰还会再打满。
一、确认报错与当前连接数
先确认是不是真的打满,而不是应用侧的偶发超时:
SHOW VARIABLES LIKE 'max_connections';
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Max_used_connections';
Threads_connected 接近 max_connections,且 Max_used_connections 也接近上限,基本可以确认是连接数打满。同时去云监控服务 CES 看 RDS 实例的连接数曲线,确认打满是从什么时候开始的、是缓慢爬升还是瞬间冲顶——缓慢爬升多半是连接泄漏,瞬间冲顶多半是突发流量或慢查询堆积。
二、按来源分组,看连接都去哪了
SELECT user, host, db, command, COUNT(*) AS cnt, MAX(time) AS max_time
FROM information_schema.processlist
GROUP BY user, host, db, command
ORDER BY cnt DESC;
重点看三类:
- 某个应用节点(host)的连接数异常高:优先查该节点的连接池配置;
- 大量
Sleep状态且time很大的连接:空闲连接没有及时释放; - 大量
Query状态且time很大的连接:慢查询正在占用连接。
也可以在服务器上快速统计各状态的连接数:
mysql -h <RDS实例内网域名> -P 3306 -u <用户名> -p -e "SHOW PROCESSLIST;" | awk 'NR>1 {print $5}' | sort | uniq -c | sort -rn | head
三、定位占着连接不放的慢查询和长事务
SELECT id, user, host, db, command, time, state, LEFT(info, 100) AS sql_text
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 10
ORDER BY time DESC;
SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started;
再结合 RDS 慢日志和 performance_schema.events_statements_summary_by_digest,找到执行次数多、平均耗时长的 SQL。确认无误后,先 KILL 掉明显异常的长查询和长事务持有的连接(执行前和业务方确认,避免误杀正在跑的批量任务):
KILL 12345;
注意:不要直接重启实例。重启会中断所有业务连接,恢复后连接还会重新打满,等于什么都没修。
四、核对应用连接池配置
连接数打满最常见的根因在应用侧。把每个应用实例的连接池上限加起来,和 max_connections 对一下账:
# 以 HikariCP 为例,核对每个应用实例的配置
grep -r "maximum-pool-size" /path/to/app/config/
grep -r "minimum-idle" /path/to/app/config/
- 应用实例数 × 单实例连接池上限,必须小于
max_connections,还要给管理连接、监控和人工排查留 10% 左右的余量; - 检查连接泄漏:把连接池的
leakDetectionThreshold打开后,日志里出现大量连接泄漏告警就是泄漏的证据,常见原因是代码里取了连接没有在 finally 里关闭; - 检查超时设置:
connectionTimeout、idleTimeout、maxLifetime配得过长,空闲连接会一直占着名额。
五、应急恢复:先把连接数降下来
KILL掉Sleep时间超过业务阈值(例如 300 秒)的空闲连接;- 临时调大
max_connections争取恢复时间。RDS for MySQL 的max_connections是动态参数,在控制台修改参数组后立即生效,无需重启实例;也可以用有权限的账号临时设置:
SET GLOBAL max_connections = 2000;
- 恢复后盯着连接数曲线和应用报错,确认不再继续上涨,再进入下一步。
六、治本:参数、连接池与监控一起调
- 按内存给
max_connections定个合理值:每个连接约占用几百 KB 到 1MB 的线程栈和网络缓冲,盲目调到上万只会把内存吃光,一般 8GB 内存的实例配 1000~2000 即可,调大前先算一遍账; - 收敛连接池:应用侧按实例数分摊上限,核心服务用独立账号,避免某个服务把连接池打满拖垮其他服务;
- 治理慢 SQL:慢查询少了,连接占用时间自然变短,这是最有效的“扩容”;
- 读写分离或代理:读多写少的场景走只读实例,连接压力大的场景可以考虑用数据库代理做连接池复用;
- 加监控告警:连接数使用率超过 80% 就告警,别等打满了才发现。
小结
连接数打满的排查顺序是:先确认打满,再分组定位来源,然后处理慢查询和长事务,核对连接池,最后才是调参数。调大 max_connections 只是争取时间的应急手段,真正的根因多半在连接泄漏和慢 SQL。把“应用实例数 × 连接池上限 < max_connections”这条账算清楚,再配上 80% 的使用率告警,同样的故障基本不会再来第二次。
- 点赞
- 收藏
- 关注作者
评论(0)