Node.js MySQL连接池出现Too many connections错误,排查连接泄漏原因
解决ER_CON_COUNT_ERROR: Too many connections的连接泄漏问题
嘿,我来帮你分析下这个连接泄漏的问题——你遇到的ER_CON_COUNT_ERROR确实很头疼,咱们一步步拆解你的代码和背后的原因:
你的手动连接管理代码里藏着泄漏风险
你觉得pool.getConnection手动流程和pool.query效果一致,但其实你的实现有几个潜在的漏洞,可能导致连接拿了却没放回去:
- 异步回调“失联”:如果MySQL服务器突然断开连接,或者驱动在处理请求时出现异常没触发
connection.query的回调(这种情况虽少见但确实存在),那connection.release()就永远不会执行,这个连接就被“丢”了,池里没了可用连接就会新建,直到触达MySQL的连接上限。 - 未捕获的同步错误:虽然
connection.query是异步方法,但如果调用它之前出现同步错误(比如sqlQuery是个无效类型,驱动内部抛出同步异常),回调直接不会执行,连接自然也得不到释放。
另外,你的错误处理也有点粗糙:直接返回Error(505)或Error(503)把原始错误信息丢了,排查问题时根本不知道到底是连接池满了、数据库挂了还是SQL写错了。
为什么pool.query能避免这个坑
mysql库自带的pool.query其实就是帮你封装了“拿连接→执行查询→放连接”的完整流程,而且内部做了更周全的错误处理:
- 不管查询成功、失败,甚至中途出了异常,它都会确保把连接放回池里,绝不会让连接“失联”。
- 内部的错误捕获机制覆盖了同步和异步场景,比手动实现靠谱得多。
两种修复方案任你选
方案1:直接改用pool.query(最推荐)
这是最简单省心的方式,把你的get方法改成这样就行:
const mysql = require('mysql') const pool = mysql.createPool({ host: sqlhost, port: sqlport, user: sqluser, password: sqlpsw, database: sqldb, charset: 'utf8mb4', connectionLimit: 10 }) module.exports = { get: function (sqlQuery, type) { return new Promise((resolve, reject) => { pool.query(sqlQuery, function (err, results) { if (err) { // 保留你的错误码,同时带上原始错误方便排查 const error = new Error(503) error.originalErr = err reject(error) } else { resolve(results) } }) }) } }
方案2:修复手动连接管理的代码
如果你非得自己管连接,那用async/await加finally块能彻底解决泄漏问题——finally会保证不管代码走成功还是错误分支,连接都会被释放:
const mysql = require('mysql') const pool = mysql.createPool({ host: sqlhost, port: sqlport, user: sqluser, password: sqlpsw, database: sqldb, charset: 'utf8mb4', connectionLimit: 10 }) module.exports = { get: async function (sqlQuery, type) { let connection try { // 异步获取连接 connection = await new Promise((resolve, reject) => { pool.getConnection((err, conn) => { if (err) { const error = new Error(505) error.originalErr = err reject(error) } else { resolve(conn) } }) }) // 异步执行查询 const results = await new Promise((resolve, reject) => { connection.query(sqlQuery, (err, res) => { if (err) { const error = new Error(503) error.originalErr = err reject(error) } else { resolve(res) } }) }) return results } catch (err) { // 把错误抛出去让上层处理 throw err } finally { // 无论如何都释放连接 if (connection) { connection.release() } } } }
额外的排查小技巧
- 检查MySQL的连接上限:看看MySQL服务器的
max_connections设置,确保它比你的连接池connectionLimit大,别被其他应用占满了连接。 - 加日志跟踪连接状态:给连接池加两个事件监听,就能看到连接的获取和释放情况,方便定位是不是真的泄漏:
pool.on('acquire', (connection) => { console.log(`连接 ${connection.threadId} 被获取`) }) pool.on('release', (connection) => { console.log(`连接 ${connection.threadId} 被释放`) }) - 排查慢查询:有时候不是泄漏,是某个查询跑太久占着连接不放,看起来像泄漏——可以查MySQL的慢查询日志看看有没有拖后腿的SQL。
内容的提问来源于stack exchange,提问作者Mattia
相关产品推荐
相关产品推荐

