You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
      }
    }
  }
}

额外的排查小技巧

  1. 检查MySQL的连接上限:看看MySQL服务器的max_connections设置,确保它比你的连接池connectionLimit大,别被其他应用占满了连接。
  2. 加日志跟踪连接状态:给连接池加两个事件监听,就能看到连接的获取和释放情况,方便定位是不是真的泄漏:
    pool.on('acquire', (connection) => {
      console.log(`连接 ${connection.threadId} 被获取`)
    })
    pool.on('release', (connection) => {
      console.log(`连接 ${connection.threadId} 被释放`)
    })
    
  3. 排查慢查询:有时候不是泄漏,是某个查询跑太久占着连接不放,看起来像泄漏——可以查MySQL的慢查询日志看看有没有拖后腿的SQL。

内容的提问来源于stack exchange,提问作者Mattia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 16:47:31