PROTOCOL_CONNECTION_LOST本质是连接被MySQL服务端主动断开后被池子误复用,主因是idleTimeoutMillis与wait_timeout严重错配;必须配置waitForConnections、connectionLimit和idleTimeoutMillis三参数,并确保pool实例全局单例、手动release、事务幂等及错误兜底。

连接池本身不等于安全操作——它只是基础容器,真正的安全来自配置、错误处理和调用方式的组合。
为什么 pool.query() 会突然抛出 PROTOCOL_CONNECTION_LOST
这不是连接池失效,而是你拿到的连接在被 MySQL 服务端主动断开后、还没来得及被池子标记为“坏”的瞬间被复用了。云数据库(如阿里云 RDS)常把 wait_timeout 设为 1800 秒(30 分钟),而连接池默认的 idleTimeoutMillis 是 10000 毫秒(10 秒),两者严重错配。
- 第一次查询成功,连接进入空闲状态
- 15 分钟后 MySQL 侧已关闭该 socket,但池子没检测到
- 下一次
pool.query()恰好复用这个连接 → 立即报PROTOCOL_CONNECTION_LOST - 此时连接池不会自动重试,业务层必须兜底
必须设的三个连接池参数
这三个值不是可选建议,是防止静默失败的底线配置:
ZenHeart zenlink 与 zenlink-mcp 为 OpenClaw,涵盖安装、MCP 注册、钩子接线、入站轮询及升级维护。
-
waitForConnections: true—— 否则高并发时请求直接丢弃,无日志、无报错、无声无息 -
connectionLimit: 10—— 别设成 100;MySQL 默认max_connections通常只有 151,超了会导致新连接被拒绝 -
idleTimeoutMillis: 1800000—— 必须比 MySQL 的wait_timeout小至少 60 秒;若不确定值,统一设为 1800000(30 分钟)最稳妥
事务中不能靠重试解决连接断开
在 START TRANSACTION 块里执行到一半遇到 PROTOCOL_CONNECTION_LOST,直接重试整个事务会破坏原子性——前半段可能已写入部分数据,再跑一遍就重复了。
- 正确做法:用
SAVEPOINT切分关键步骤,失败后回滚到最近保存点 - 或改用幂等设计:比如插入前先查主键是否存在,更新时加
WHERE version = ?条件校验 -
pool.getConnection()后必须手动conn.release(),否则连接永远卡在“占用”状态,池子会慢慢枯竭
别让 unhandledRejection 杀死进程
用 mysql2/promise 时,所有 await pool.query() 都必须包裹 try/catch。漏掉一个,PROTOCOL_CONNECTION_LOST 就会触发 unhandledRejection,Node.js 进程直接退出——看起来像数据库挂了,其实是 JS 异常没捕获。
- 全局
process.on('unhandledRejection', ...)只能做告警,不能替代业务层catch - 错误日志里重点看
err.code(如PROTOCOL_CONNECTION_LOST)和err.fatal(true 表示连接级崩溃) - 连接池实例必须是全局单例;在路由文件里反复
createPool(),等于每请求新建一套池子,内存和连接数都会失控
最容易被忽略的是:连接池创建完成 ≠ 数据库连通。它只是一张“用工合同”,真正干活要等到第一次 getConnection() 或 query() 才走 TCP 握手。线上部署后第一笔请求失败,大概率就卡在这一步。

















