SHOW PROCESSLIST 里出现大量 Command 为 Sleep 的会话,Threads_connected 接近 max_connections,但 CPU、QPS 都不高——直觉反应常是“连接池配小了”,于是调大上限。这个方向经常是错的,先看 wait_timeout 更省事。

先理解 Sleep 是什么

Sleep 表示连接已建立、当前没有语句在执行,服务端在等客户端发下一条命令。它不占 CPU,问题是空闲连接不被回收,占住 max_connections 名额。

回收靠两个变量:

SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';

wait_timeout 作用于非交互式连接(大多数驱动和连接池),interactive_timeout 作用于交互式连接(mysql 命令行默认是交互式)。默认值通常都是 28800 秒,即空闲连接要躺满 8 小时才被断开。

判断方向:Sleep 连接的 Time 普遍只有几十秒到几分钟,说明服务端在正常回收,问题在客户端不断新建连接;能累积到几千、几万秒,说明回收周期太长,连接池又默认保持长连接,两边都不主动断。

具体怎么做

确认当前值,以 GLOBAL 为准,SESSION 可能被客户端连接时覆盖:

SHOW GLOBAL VARIABLES LIKE '%timeout%';
SHOW SESSION VARIABLES LIKE 'wait_timeout';

看 Sleep 连接的时长分布:

SELECT COMMAND, COUNT(*) AS cnt, MIN(TIME) AS min_s, MAX(TIME) AS max_s
FROM information_schema.PROCESSLIST
WHERE COMMAND = 'Sleep'
GROUP BY COMMAND;

max_s 远大于 wait_timeout,说明有连接绕过了超时;明显小于,说明连接是被客户端关掉的,服务端超时不是瓶颈。

按业务容忍度调低全局值:

SET GLOBAL wait_timeout = 600;
SET GLOBAL interactive_timeout = 600;

只影响之后新建的会话,已有连接需重连才生效。持久化写进 my.cnf 的 [mysqld] 段:

[mysqld]
wait_timeout = 600
interactive_timeout = 600

再对齐连接池。以 HikariCP 为例,maxLifetime(默认 1800000 毫秒)应小于服务端 wait_timeout,否则池子会把已被服务端断开的连接当可用连接借出,触发 Communications link failure 或 The last packet successfully received from the server was ... milliseconds ago。

为什么先看它有效

连接池负责复用连接、控制并发,不负责判断服务端何时踢掉连接。服务端超时和客户端保活是两套独立机制,任何一边假设错了,另一边都表现为连接数异常。把 wait_timeout 调到与业务空闲周期匹配(比如 10 分钟),再让 maxLifetime 略小于它,空闲连接会被按时回收,池子按需重建。反过来,先盲目调大连接池,只是把更多连接塞进 8 小时的等待队列。

怎么确认生效

SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Aborted_clients';

Threads_connected 应稳定在并发量附近而非持续爬升。Aborted_clients 明显上升,说明客户端在服务端断开后仍在用旧连接,回头检查 maxLifetime 和 testOnBorrow 类配置;应用日志里的 Communications link failure 是同一类信号。

边界与副作用

wait_timeout 不能设得过小。低于业务最长空闲间隔时,连接会在两次请求之间被断开,客户端下次使用报错,表现为间歇性失败,比连接数高更难排查。一般不低于 60 秒,常见取值 300 到 1800 秒。

它只解决“空闲连接不被回收”,不解决连接泄漏。Sleep 连接 Time 都很短但总数持续增长,说明应用反复建连而不复用,调超时没用。长事务或 LOCK 等待导致的连接堆积,Command 不是 Sleep,要看 State 字段区分。

云数据库的 wait_timeout 可能由控制台参数模板管理,直接 SET GLOBAL 会被覆盖或拒绝。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注