mysql从库大量sleep进程与主从同步无关,本质是应用连接池未合理管理连接:conn.close()仅归还连接至池中而非物理断开,若idletimeout过长、maxidle过大或未启用保活机制,将导致大量空闲连接长期维持sleep状态;需协同调优连接池参数与wait_timeout(建议后者略大于前者),并排查应用连接泄漏。

从库上大量Sleep进程和主从同步本身无关
MySQL从库出现大量Sleep进程,不是因为主从复制在工作,而是因为有客户端(比如应用服务)连到了从库,并且没及时断开。主从同步走的是专用的I/O Thread和SQL Thread,它们的状态是Waiting for master to send event或Slave has read all relay log,不是Sleep。所以看到从库SHOW PROCESSLIST里一堆Sleep,基本可以排除是复制机制导致的。
真正原因往往是应用层连接池没配好
现代应用几乎都用连接池(如HikariCP、Druid、DBCP),而连接池默认行为是「归还连接」而非「物理关闭」。也就是说,你代码里调了conn.close(),连接只是回到池子里待命,MySQL侧依然维持着这个TCP连接,状态就是Sleep。如果池子配置不合理,就容易堆积:
-
maxIdle设得过大,空闲连接长期不销毁 -
minIdle设得过高,冷启动后立刻保底一堆空闲连接 - 没配
idleTimeout或设得过长(比如默认30分钟),导致空闲连接迟迟不被驱逐 - 连接池启用了「测试连接有效性」但没配
validationQuery或testOnBorrow,导致失效连接滞留并最终变成Sleep超时前的僵尸连接
查连接来源比查SQL更有效
别一上来就看慢查询日志——Sleep进程本身不执行SQL。优先定位是谁连上来的:
- 执行
SHOW PROCESSLIST,重点看User和Host列,确认是不是你的应用账号(比如app_ro@10.20.30.%) - 用
SELECT USER(), CURRENT_USER()区分认证用户和实际连接用户 - 检查应用日志里是否频繁打印「acquired connection」但没对应「released」,说明连接泄漏
- 抓包或用
netstat -antp | grep :3306确认连接确实来自应用服务器IP,排除DBA工具(如DBeaver/Navicat)残留连接
wait_timeout改太小反而会引发“MySQL has gone away”
很多文章建议把wait_timeout从默认28800秒(8小时)直接砍到300秒(5分钟),这在连接池场景下非常危险:
- 连接池里的连接被MySQL主动断开后,池子并不知道,下次借出仍当有效连接用,立刻报
MySQL server has gone away - 必须配套开启连接池的保活机制:比如HikariCP的
connection-test-query=SELECT 1+idle-timeout=600000+max-lifetime=1800000 -
wait_timeout建议设为略大于连接池的idleTimeout(例如池子设10分钟空闲回收,MySQL设12分钟),留出安全余量 - 修改务必用
SET GLOBAL wait_timeout = 720,然后验证:SHOW GLOBAL VARIABLES LIKE 'wait_timeout',别只看SHOW VARIABLES(那是会话级)
真正要盯住的不是Sleep数量本身,而是这些Sleep连接背后的生命周期管理逻辑——连接池有没有在它该释放的时候释放,MySQL有没有在它该断的时候断,两者节奏错位,才是问题根因。











