Workerman中不能直接使用mysqli或PDO长连接,因其阻塞、有状态、不支持自动重连且无法跨进程共享;推荐使用workerman/mysql异步客户端,支持连接池、自动重连和超时控制。

Workerman 里不能直接用 mysqli 或 PDO 长连接连 MySQL
Workerman 是常驻内存的异步 PHP 框架,而 mysqli 和 PDO 的默认连接是阻塞式、有状态的,一旦连接被某个进程复用或超时断开,后续请求会直接报错(比如 MySQL server has gone away)。更关键的是:Workerman 的子进程(Worker)会持续运行,但 MySQL 连接不会自动重连,也不会跨进程共享。
所以你不能写这样的代码:
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', $user, $pass);
// 然后在 onMessage 里反复用 $pdo->query(...) —— 这会崩- 每次请求前必须检查连接是否还活着(
$pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS)不可靠) - 连接超时(
wait_timeout默认 8 小时,但中间网络抖动、防火墙中断等都会提前 kill 连接) - 多进程下每个 Worker 持有自己的连接,无法复用,容易打爆 MySQL 的
max_connections
推荐方案:用 workerman/mysql 异步协程客户端
官方维护的 workerman/mysql 是基于 MySQL 协议实现的纯 PHP 异步客户端,支持连接池、自动重连、超时控制,且不依赖 ext-mysqlnd 或 libmysqlclient。它和 Workerman 的事件循环天然兼容。
安装:
立即学习“PHP免费学习笔记(深入)”;
composer require workerman/mysql
基本用法(在 Worker 启动时初始化连接池):
use Workerman\MySQL\Connection;$mysql = new Connection([ 'host' => '127.0.0.1', 'port' => 3306, 'user' => 'root', 'password' => '123456', 'database' => 'test', 'charset' => 'utf8mb4', 'read_timeout' => 5, // 读超时秒数 'write_timeout' => 5, ]);
// 查询示例(注意:返回的是 Promise,需在回调中处理) $mysql->query('SELECT * FROM user WHERE id = ?', [1], function($result) { var_dump($result); });
- 连接池默认大小为 10,可通过
'pool_size' => 20扩容 - 所有查询都是非阻塞的,不会卡住 EventLoop
- 不支持预处理语句的 bindParam/bindValue,参数直接以数组传入
query() - 不支持事务嵌套;开启事务后,必须在同一个
Connection实例上调用begin()/commit(),不能跨实例
如果坚持用 PDO,必须按请求新建 + 显式销毁
仅适用于低并发、调试或简单脚本场景。核心原则:**绝不复用连接对象,每个请求 new 一次,用完 unset**。
// ✅ 正确(在 onMessage 内部创建)
public function onMessage($connection, $data)
{
try {
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'root', '123456', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare('SELECT * FROM user WHERE id = ?');
$stmt->execute([$id]);
$user = $stmt->fetch();
$connection->send(json_encode($user));
} catch (Exception $e) {
$connection->send('db error');
} finally {
// ⚠️ 必须显式销毁,触发析构关闭连接
unset($pdo);
}
}
- 每次 new PDO 都会建立新 TCP 连接,性能差、易触发 MySQL 连接数上限
- 务必设置
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,否则错误静默失败 - 不要设
PDO::ATTR_PERSISTENT => true,持久连接在 Workerman 下完全不可控,极易导致连接泄漏
连接池配置与监控要点
用 workerman/mysql 时,连接池行为不像 Swoole 那样透明,容易误判“连不上”是网络问题还是池耗尽。几个关键点:
- 连接池满时,默认行为是阻塞等待(可配
'pool_wait_timeout' => 3秒后抛异常) - 连接空闲超时(
'pool_idle_timeout' => 60)后会被自动回收,避免僵死连接 - 上线前务必用
netstat -an | grep :3306 | wc -l观察实际建连数,确认没超出 MySQL 的max_connections - 建议加一层简单健康检查:启动时执行
$mysql->query('SELECT 1'),失败则exit(1),避免 Worker 带病运行
最常被忽略的是连接池大小和 MySQL 侧的 wait_timeout 不匹配——比如池设了 50,但 MySQL 只允许 30 连接,结果一半请求永远卡在等待池释放,日志里却只显示“timeout”,不是“connection refused”。











