跳到主要内容

连接池问题排查

问题

应用频繁出现「获取连接超时」错误,如何排查和解决?

答案

一、连接池核心原理

二、常见错误与原因

错误信息常见原因
Connection pool exhausted连接被用完,未及时归还
Connection is not available, request timed out等待连接超时
Too many connections超过 MySQL max_connections
Connection reset by peer空闲连接被 MySQL 关闭
Communications link failure网络中断或 MySQL 重启

三、排查步骤

步骤 1:确认 MySQL 侧连接数

-- 当前连接数
SHOW STATUS LIKE 'Threads_connected';
-- 最大连接数
SHOW VARIABLES LIKE 'max_connections';
-- 各状态连接数
SHOW PROCESSLIST;

-- 各用户/Host 的连接分布
SELECT user, host, COUNT(*) AS conn_count
FROM information_schema.PROCESSLIST
GROUP BY user, host
ORDER BY conn_count DESC;

步骤 2:确认连接池配置

// Node.js mysql2 连接池示例
const pool = mysql.createPool({
host: 'localhost',
user: 'app',
database: 'mydb',
// 关键参数
connectionLimit: 10, // 最大连接数
waitForConnections: true, // 超过最大数时等待
queueLimit: 0, // 等待队列无限制
connectTimeout: 10000, // 连接建立超时 10s
idleTimeout: 60000, // 空闲超时 60s
});

HikariCP(Java)关键参数

参数推荐值说明
maximumPoolSizeCPU 核心数 × 2 + 1最大连接数
minimumIdle= maximumPoolSize最小空闲连接
connectionTimeout30000ms获取连接超时
idleTimeout600000ms空闲连接超时
maxLifetime1800000ms连接最大存活时间
leakDetectionThreshold60000ms连接泄漏检测
连接数 ≠ 越大越好

MySQL 官方建议公式:连接数 = CPU 核心数 × 2 + 磁盘数

过多连接导致:

  1. 线程上下文切换开销增大
  2. MySQL 内存占用增加(每连接约 1~10MB)
  3. 锁竞争加剧

步骤 3:检查连接泄漏

连接泄漏 = 获取连接后未归还,是最常见的连接耗尽原因。

// ❌ 错误:异常时连接未释放
async function queryBad() {
const conn = await pool.getConnection();
const result = await conn.query('SELECT * FROM users'); // 若抛异常,下面不执行
conn.release(); // 永远不会执行
return result;
}

// ✅ 正确:try-finally 确保释放
async function queryGood() {
const conn = await pool.getConnection();
try {
return await conn.query('SELECT * FROM users');
} finally {
conn.release(); // 无论成功失败都释放
}
}

// ✅ 更好:使用 pool.query(自动获取和释放连接)
async function queryBest() {
return pool.query('SELECT * FROM users');
}

步骤 4:检查慢查询占用连接

-- 查看运行中的慢查询
SELECT id, user, host, db, command, time, state, info
FROM information_schema.PROCESSLIST
WHERE command != 'Sleep' AND time > 5
ORDER BY time DESC;

慢查询长时间占用连接 → 可用连接减少 → 其他请求超时。

四、连接池监控

// 定时打印连接池状态
setInterval(() => {
const status = pool.pool;
console.log({
total: status._allConnections.length, // 总连接数
free: status._freeConnections.length, // 空闲连接数
queued: status._connectionQueue.length, // 等待队列长度
});
}, 5000);

五、空闲连接被 MySQL 关闭

-- MySQL 默认 8 小时关闭空闲连接
SHOW VARIABLES LIKE 'wait_timeout'; -- 默认 28800 秒

解决方案

方案说明
连接池心跳检测定期发送 SELECT 1 验活
maxLifetime < wait_timeout主动回收,避免使用已断开的连接
重连机制获取连接时检测有效性

常见面试问题

Q1: 连接池大小应该怎么配置?

答案

  1. 经验公式connections = CPU 核心数 × 2 + 有效磁盘数
  2. 实际考量
    • 微服务实例数 × 每个实例连接数 < MySQL max_connections
    • 例:10 个实例,每个 20 连接 = 200,MySQL 需设 250+ 留余量
  3. 压测验证:逐步增大连接数,观察 TPS 和 RT

Q2: 如何排查连接泄漏?

答案

  1. HikariCP:开启 leakDetectionThreshold,超时未归还会打印获取连接的堆栈
  2. 监控连接池:活跃连接持续增长而不下降 = 泄漏信号
  3. 代码审查:搜索 getConnection 确保对应 release / close
  4. MySQL 侧SHOW PROCESSLIST 查看 Sleep 状态连接是否异常多

Q3: MySQL max_connections 被打满怎么办?

答案

  1. 紧急处理KILL 空闲连接释放资源
  2. 临时调大SET GLOBAL max_connections = 500
  3. 排查根因:连接泄漏?连接池配置过大?慢查询占用?
  4. 长期优化:引入读写分离、缓存降低数据库访问

相关链接