“MySQL server has gone away”主因是协程间错误复用连接、MySQL服务端wait_timeout断连、客户端未校验状态三者叠加;修复核心为每次query前ping校验、ping失败则新建连接、归还时不close、pop超时设500ms、禁用Coroutine::close。

网络中断导致事务失效,本质不是“事务没回滚”,而是连接断开后事务状态丢失、无法继续控制。Swoole 4 协程中 MySQL 事务必须在**同一个连接、同一个协程、连续执行**的上下文中完成,一旦底层 TCP 断开(如超时、服务端 kill、网络抖动),连接对象就进入不可用状态,BEGIN 后未 COMMIT 或 ROLLBACK 的事务会被 MySQL 服务端自动回滚——但这属于服务端被动清理,不属于你的业务可控逻辑。
事务前必须确保连接有效
不能依赖 $mysql->connected 判断连接是否可用,它只反映上一次 connect 结果,不感知服务端断连。每次执行事务操作前,必须调用 ping() 主动探测:
if (!$mysql->ping()) { $mysql = new Swoole\Coroutine\MySQL(); $mysql->connect($config); }- ping 失败后禁止对原实例调用
connect(),状态机已损坏,必须新建连接 - 该检测需包裹在
try/finally中,确保无论成功失败都归还或丢弃连接
事务过程必须原子且不跨协程切换
从 begin() 到 commit() 或 rollback() 必须由同一个协程使用同一个连接实例完成:
- 禁止在事务中途
pop()新连接、或把连接传给其他协程 - 禁止把连接存为类属性、静态变量或全局变量供多协程复用
- 若事务中发生网络中断,
query()会直接报错(如 errno=2013),此时应立即rollback()并捕获异常
连接池配置要匹配事务生命周期
连接池不是万能的,错误配置反而加剧问题:
-
pop()超时建议设为500毫秒,避免协程卡死在获取连接上 - 归还连接时不要调用
$mysql->close(),只需$pool->push($mysql) - 连接池内部应支持健康检查,对 ping 失败的连接自动丢弃而非归还
- 事务场景下,连接不应被其他协程复用,推荐设置
max_idle_time = 0或极短值(如 1 秒)防止空闲连接过期
异常处理必须显式回滚
任何 SQL 执行失败、超时、连接中断,都必须触发 rollback(),不能依赖 MySQL 自动清理:
- 用
try/catch包裹整个事务块,catch中优先$mysql->rollback() - 即使
rollback()本身也失败(如连接已断),也要记录日志并标记事务异常终止 - 避免使用
CONTINUE HANDLER类存储过程逻辑替代应用层事务控制,协程环境不适用


















