swoole\coroutine\mysql是唯一真协程mysql客户端,因原生mysqli/pdo底层阻塞i/o无法被调度;必须显式connect、设charset、用数组结果,且不可跨协程复用连接。

为什么 swoole_mysql 已被废弃,但面试还常问?
因为它是理解 Swoole 异步 I/O 模型演进的关键切口。现在官方主推 co::mysql(协程 MySQL 客户端),但很多老项目、中间件或面试官仍会拿 swoole_mysql 的设计逻辑考你对「非阻塞回调」和「连接复用」的理解。
它不是让你去写,而是看你能不能说清:为什么不能直接用 mysqli?为什么必须手动管理连接池?为什么回调里不能 throw 异常?
-
swoole_mysql是纯异步 + 回调模式,不依赖协程,底层基于 epoll/kqueue 直接操作 socket - 它没有自动重连,没有查询超时控制(得自己
Timer::after),也没有事务上下文 - 一旦连接断开,
onClose触发后,该对象就不可再用——常见错误是回调里还试图$db->query()
co::mysql 查询失败时,getLastError() 为什么经常为空?
因为协程客户端的错误分两类:连接阶段失败(如 DNS 解析失败、TCP 握手超时)和查询执行失败(如 SQL 语法错、主键冲突)。前者会抛出 Throwable,后者才走 getLastError()。
更关键的是:协程方法默认不抛异常,得显式开启 set(['throw_exception' => true]),否则 query() 返回 false,但你不检查返回值就直接取结果,就会静默失败。
- 连接失败时,
new Co\MySQL()或connect()会直接 throw,不会等query() - 查询失败时,
getLastError()只在query()返回false后有效;若开了throw_exception,则根本不会走到这一步 - 注意
getLastError()是实例方法,不是静态方法;多个连接共用一个变量名时容易误读上一个连接的错误
用 Co\MySQL 做连接池,为什么不能简单 new 多个实例丢进数组?
因为协程客户端不是线程安全的,也不是“可共享”的资源。每个 Co\MySQL 实例绑定当前协程上下文,跨协程调用会触发致命错误:Fatal error: Uncaught Swoole\Error: Socket#N is not available。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
真正可用的连接池必须满足:获取连接 → 绑定到当前协程 → 使用完毕 → 归还(并清理状态,比如 reset() 或 close())。
- 不要用
array_push($pool, new Co\MySQL())然后array_shift()—— 协程切换后该句柄已失效 - 推荐用
Swoole\Coroutine\Channel管理空闲连接,pop()出来的实例只在当前协程内使用,用完push()回去 - 归还前务必调用
$mysql->close()或$mysql->reset(),否则事务状态、用户变量可能污染下一个请求
协程 MySQL 查询里能用 sleep() 或 usleep() 吗?
不能。它们会阻塞整个进程(因为是 PHP 原生函数),直接杀死协程调度。Swoole 提供的是 Co::sleep() 和 Co::usleep(),底层触发让出协程控制权,不占 CPU。
这个点常被忽略,尤其在模拟重试逻辑、防刷限流或调试时随手写 sleep(1),结果整个 worker 进程卡住,QPS 断崖下跌。
-
sleep()/usleep()→ 阻塞当前线程 → 所有协程暂停 -
Co::sleep()/Co::usleep()→ 让出当前协程 → 其他协程继续跑 - 如果用了第三方库(比如某些 SDK 封装了
sleep),要确认它是否适配协程环境;不确认就加hook_flags = SWOOLE_HOOK_ALL
异步 MySQL 的核心从来不是 API 怎么写,而是你脑子里有没有那根「IO 不会等」的弦——连接、查询、等待、错误,每一步都得想清楚控制权在谁手里。很多人卡在“为什么我查不到数据”,其实只是忘了 yield 或漏了 if ($result === false)。










