workerman中数据库操作卡住主线程等问题源于未适配常驻进程模型:必须在onworkerstart复用workerman\mysql\connection连接,显式设utf8mb4字符集,禁用pdo持久连接,事务须在同一回调内完成,查询前用ping检测连接存活。

Workerman中数据库操作卡住主线程、连接数暴涨、事务丢失、emoji存不进去——这些问题都源于没搞清常驻进程模型下数据库该怎样用。你不是缺连接池,而是没把单连接复用到位,也没避开PDO和mysqli的坑。
先装好基础依赖再动手
运行 php -m 检查是否已启用 pdo 和 pdo_mysql 扩展。缺任何一个都会报 Undefined class constant 'MYSQL_ATTR_INIT_COMMAND' 错误。
Windows 下打开 php.ini,取消这两行前的分号:extension=php_pdo.dllextension=php_pdo_mysql.dll
Linux 下若用 apt 包管理器,执行:sudo apt-get install php-mysql;若提示找不到包,改用 apt-cache search php mysql 查准确包名。
【必须在 CLI 环境下验证】:别只在 Web 服务器里测,Workerman 是命令行启动的,php -m 输出必须包含 pdo 和 pdo_mysql,否则后续所有连接都会失败。
用 Workerman\MySQL\Connection 建立稳定连接
这是 Workerman 官方推荐的异步 MySQL 客户端,底层走纯异步 I/O,不是 PDO 封装。
第一步:用 Composer 安装composer require workerman/mysql
第二步:在 onWorkerStart 中初始化连接,**绝不能放在 onMessage 或 onConnect 里**——否则每次请求都 new 一次,50 个 Worker 进程就会撑爆 MySQL 的 max_connections。
第三步:配置关键参数,不设就容易掉链子:
• charset 必须显式设为 utf8mb4,否则 emoji 或四字节字符入库会截断或报错
• read_timeout 和 write_timeout 设为 3~5 秒,太短误判超时,太长拖垮 EventLoop
示例代码:
$worker->onWorkerStart = function ($worker) {<br> global $db;<br> $db = new Workerman\MySQL\Connection('127.0.0.1', 3306, 'root', 'pass', 'test');<br> $db->charset = 'utf8mb4';<br> $db->read_timeout = $db->write_timeout = 4;<br>};
查询、插入、更新怎么写才不出错
方法一:用 query() 执行原生 SQL(最常用)$result = $db->query("SELECT id,name FROM user WHERE status = ?", [1]);
注意:Workerman\MySQL\Connection 不支持预处理绑定语法(如 bindParam),参数必须以数组形式传入 query() 第二个参数。
方法二:用 insert() / update() / delete() 链式调用(更安全)$db->insert('user')->cols(['name' => 'Alice', 'age' => 28])->query();
这一步会自动转义字段和值,避免 SQL 注入,比拼字符串更可靠。
方法三:执行带事务的操作
必须在同一个 $db 实例上调用:
$db->begin();<br>if ($db->insert('log')->cols(['msg' => 'start'])->query() === false) {<br> $db->rollback();<br> return;<br>}<br>$db->commit();
【跨回调事务必然失效】:begin 在 onMessage A 里,commit 在 onMessage B 里——事务不会提交,且后续请求会被锁住。
连接断开后自动恢复的实操方案
MySQL 默认 wait_timeout=28800(8 小时),空闲连接会被踢掉,下次 query 报 MySQL server has gone away。
第一步:捕获异常并重试一次
Workerman\MySQL\Connection 内置了自动重试逻辑,但仅限于 server has gone away 类错误,其他错误(如权限拒绝、表不存在)不会重试。
第二步:手动加 ping 检测(更可控)
每次 query 前加一句:if (!$db->ping()) { $db = new Workerman\MySQL\Connection(...); }
这比等报错再重建更主动,也避免了重试延迟。
第三步:千万别用持久连接PDO::ATTR_PERSISTENT => true 在 Workerman 下反而加剧连接泄漏,因为 PHP-FPM 的生命周期模型和常驻进程完全不兼容。
绝对不能在协程里混用 PDO 或 mysqli
Workerman 默认运行在同步阻塞模型上。哪怕你启用了 Swoole 或 Fiber 驱动,只要用了 PDO 或 mysqli 这类同步扩展,整个协程就会被卡住——它们底层调用的是阻塞 socket API。
如果你硬要用 mysqli,请确保:
• 在 onWorkerStart 初始化 $this->db = new mysqli(...)
• 每次查询前调 $this->db->ping() 检测存活
• 绝对不要在回调里调 $mysqli->close(),关了就真没了
而 PDO 在 Workerman 场景下风险更高:prepare 语句不能跨请求复用,事务边界难控制,且不支持惰性重连机制。











