mysql server has gone away 根本原因是连接已断但 $mysql->connected 仍返回 true;必须每次 query 前调用 ping() 探测,失败则新建连接,禁用 pdo 持久化,显式调用 db::pool(),配置仅限 config/swoole.php 的 db_pool 块。

不是“连接池失效”,而是协程模型下连接复用逻辑没对齐 MySQL 的真实状态——MySQL server has gone away 本质是连接已断、但 PHP 对象还假装活着。
为什么 $mysql->connected 完全不可信
这个属性只在 connect() 成功时设为 true,之后从不更新;哪怕 MySQL 服务端因 wait_timeout(默认 8 小时)主动断连,或被 KILL,它仍返回 true。协程切出再切回时,直接复用这个“假连接”就会触发错误。
- 别用
if ($mysql->connected) { $mysql->query(...) }—— 这是典型误用 -
ping()是唯一可靠探测:走 COM_PING 包,不触发权限检查,开销极小 - 必须每次
query()前调用,不能省,不能合并到初始化里
ping 失败后必须 new \Swoole\Coroutine\MySQL(),不能 connect()
已失效的实例内部协议状态机已错乱,强行 connect() 可能 panic 或返回静默失败。安全做法是彻底丢弃旧对象,新建一个再连。
- 示例逻辑必须包裹在
try/finally中,确保无论成功失败都归还(或丢弃) - 不要在
push()前手动close()—— 归还时不 close,否则 fd 泄漏、time_wait 堆积 - 连接池配置中需禁用
Coroutine::close(),避免协程销毁时误关 fd
pop() 超时别设 0,500ms 是安全底线
设 $pool->pop(0) 看似“立即返回”,实际高并发下大量协程卡死在阻塞等待,CPU 拉满却无响应。
- 超时设为
500(毫秒),超时抛异常,业务层可降级或重试 -
max_active别硬套 MySQL 的max_connections,要按QPS × 平均 SQL 耗时 × 1.5计算 - 若 MySQL
max_connections = 150,Worker 数为 4,则每个 Worker 的max_active最高建议 ≤ 32
ThinkPHP 8.1 下还要额外注意驱动和配置位置
TP8.1 的协程连接池不是靠 config/database.php 配置生效的,它依赖扩展版本、驱动类型、配置路径三者严格匹配。
- 必须用
'type' => 'mysqlnd',禁用所有PDO::ATTR_PERSISTENT - 连接池参数只能写在
config/swoole.php的db_pool块里,其他地方无效 - 代码中必须显式调用
Db::pool(),否则模型查询绕过池子直连,照样触发Too many connections
最易被忽略的一点:MySQL 的 wait_timeout 和应用层 ping 校验之间存在时间窗口,哪怕你 ping 成功了,下一秒也可能断——所以校验必须紧贴 query,不能提前缓存结果,也不能靠定时器“保活”。











