workerman中mysql单例连接需带健康检查与状态隔离:必须ping检测连接存活,禁止跨请求复用事务/变量,重试死锁须用干净连接,避免会话级状态污染。

WorkerMan 不会在每次请求后销毁全局对象或类的静态成员,所以单例实例(比如数据库连接)在整个进程生命周期内被复用。这本身能提升性能,但并发下极易出问题。
MySQL 连接被服务端主动断开(MySQL server has gone away)
数据库服务器默认会在空闲一段时间(如 8 小时)后关闭闲置连接;而 WorkerMan 进程常驻,静态持有的 MySQL 连接可能早已失效,但代码仍直接调用 query() 或 execute() —— 此时就会报错 MySQL server has gone away。
- 不能依赖“连接一次、永久复用”,必须在使用前检测连接是否活跃(例如执行
PING或捕获异常后重连) - 避免把
new PDO()或mysqli实例直接塞进static $instance,而应封装成带健康检查的代理对象 - 某些 MySQL 驱动(如
workerman/mysql)内部已做自动重连,但需确认其版本是否启用该行为(默认可能关闭)
多个请求共用同一连接引发状态污染
MySQL 连接是会话级的:事务状态、用户变量、临时表、字符集、SQL mode 等都绑定在连接上。如果两个并发请求通过同一个单例连接先后开启事务,第二个请求可能意外继承第一个未提交/未回滚的事务上下文。
- 尤其在 Web 请求 + 定时器混合场景下,
onWorkerStart初始化的单例连接,可能被定时任务和 HTTP 请求交替使用 - 不要在单例中保存任何会话级状态(如
SET @var = ...、START TRANSACTION后不及时COMMIT) - 更稳妥的做法是:每个业务逻辑单元(如一次 API 调用)获取独立连接(通过连接池),而非复用静态实例
死锁重试时共享连接导致事务交叉失败
当捕获到 1213(Deadlock)错误并尝试重试时,若重试逻辑仍走同一个单例连接,而该连接上已有未清理的事务状态(如部分语句已执行、锁未释放),重试就会失败或产生不可预知行为。
- 重试必须在干净连接上进行 —— 比如先
$pdo->rollback(),再unset($pdo)或显式 close,然后重建 - 若用
workerman/mysql,注意其ConnectionPool的get()返回的是新租约连接,不是静态单例,更适合重试场景 - 自建单例 + 手动重试时,务必确保
try/catch块内完成连接清理,而不是只处理 SQL 逻辑











