必须在onWorkerStart中单进程初始化MySQL连接并复用,禁用PDO持久连接;因Workerman常驻内存,onMessage内新建连接会累积触发“MySQL server has gone away”或“Too many connections”。

Workerman 4.0.41 中复用 MySQL 连接必须避开 onMessage 内新建实例的陷阱,否则运行几小时后必然触发 “MySQL server has gone away” 或 “Too many connections”,这是因为常驻进程持续累积未释放的连接句柄,而 MySQL 服务端已主动关闭空闲超时连接。
为什么不能在 onMessage 里 new PDO 或 new Connection
Workerman 是常驻内存的异步框架,onMessage 会被反复调用;每次 new 一个 PDO 或 Connection 实例,旧连接不会自动 close,子进程生命周期内持续堆积——【连接数会在 2~3 小时内突破 MySQL 默认 max_connections=151】。这不是网络断开,是连接被 MySQL 主动踢掉后仍强行复用导致的静默失败。
PHP-FPM 每次请求新建进程,连接随进程销毁自然释放;Workerman 完全是另一套生命周期,必须人为控制连接归属范围。
正确初始化位置:onWorkerStart 中单进程独占连接
第一步:确认 PHP CLI 已启用 pdo 和 pdo_mysql 扩展,执行 php -m | grep -E 'pdo|mysql' 应同时输出 pdo、pdo_mysql;缺失则需安装,CentOS 7 用 yum install php70w-pdo php70w-mysqlnd,Ubuntu 22.04 用 apt-get install php-mysql。
第二步:在 $worker->onWorkerStart 回调中初始化 Connection 实例,并赋值给全局变量或 $worker->db 属性,确保该 Worker 子进程内唯一复用。
第三步:初始化后立即调用 $db->ping(),避免首次 query 因连接已失效而直接报错;ping 失败则 unset 原实例并重建一次。
这一步操作起来很简单,直接把连接实例挂到 $worker 对象上就行,但漏掉 ping 就会导致第一条 SQL 必然失败。
两种安全复用方式
方法一:使用 workerman/mysql 官方异步客户端(推荐)
① 通过 Composer 安装:composer require workerman/mysql;
② 初始化时传入完整配置数组,必须显式指定 'charset' => 'utf8mb4',否则中文插入会乱码或失败;
③ 所有 query 调用返回 Promise,必须用回调处理结果,不可同步等待;示例:$db->query('SELECT id FROM user WHERE status = ?', [1], function($result) { var_dump($result); });
方法二:手动管理 PDO 实例(仅限低并发调试场景)
必须在 onWorkerStart 中创建,并设置 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION;
每次 query 前手动检测连接状态:try { $pdo->query('SELECT 1'); } catch (PDOException $e) { if (strpos($e->getMessage(), 'MySQL server has gone away') !== false) { $pdo = new PDO(/* 重连参数 */); } };
【严禁开启 PDO::ATTR_PERSISTENT => true】,持久连接在 fork 后会被子进程继承失效句柄,引发不可预测的读写错误。


















