Swoole通过常驻内存、协程和异步非阻塞I/O彻底重构PHP运行模型:避免FPM重复加载与编译,复用连接池及全局资源,协程并发使I/O等待不阻塞主线程,QPS提升最高达11倍,但需警惕静态属性、全局变量等引发的状态污染与内存泄漏。

因为 Swoole 不是“换了个更快的 PHP”,而是彻底改写了 PHP 的运行生命周期和 I/O 模型。它用常驻内存 + 协程 + 异步非阻塞,绕开了 PHP-FPM 每次请求都重复加载、创建连接、销毁资源的硬伤。
为什么每次请求都要重连数据库?
PHP-FPM 下,new mysqli() 或 $redis->connect() 在每个请求里都是全新发起的 TCP 连接。三次握手、认证、释放连接全得重来一遍——高并发时数据库连接数暴涨,甚至触发 max_connections 限制。
- Swoole 启动时就建立好连接池(比如
Swoole\Coroutine\MySQL),请求只从池中取空闲连接,毫秒级复用 - 连接池大小可配(
pool->set(['min' => 5, 'max' => 50])),避免连接爆炸或饥饿 - 传统模式下连接对象随请求销毁,Swoole 中连接对象常驻 Worker 进程内存,但协程间隔离,不会互相污染
为什么 Swoole 不用反复编译 PHP 脚本?
PHP-FPM 每次请求都要走完整流程:open → compile → execute → close,其中 compile 阶段把 PHP 源码转成 Zend VM 的 opcode,开销不可忽略;而 Swoole 的 Worker 进程启动后,代码只编译一次,后续所有协程直接执行已缓存的 opcode。
- 这意味着框架初始化(如 Composer autoloader、配置加载、路由注册)也只做一次
- 全局单例(如
Container、Logger)无需在每个请求里重建,直接复用 - 注意:若在
onRequest回调里require动态文件,仍会触发重复加载,应提前预加载或使用协程版file_get_contents
为什么并发能力差距能到 11 倍?
关键在 I/O 等待是否阻塞主线程。PHP-FPM 遇到 curl_exec、mysqli_query 就卡住,只能等;Swoole 的协程遇到 co::sleep、mysql->query、http_client->get 会自动让出控制权,Worker 可立即处理其他协程。
立即学习“PHP免费学习笔记(深入)”;
- 一个请求并行调 3 个 HTTP 接口:FPM 要串行耗时约 3×300ms = 900ms;Swoole 协程并发,总耗时 ≈ max(300ms, 300ms, 300ms) = 300ms
- 底层靠
epoll/kqueue监听 socket 状态,不轮询、不空转,CPU 利用率更健康 - 协程切换开销约 50ns,远低于线程(μs 级)或进程(ms 级)
容易被忽略的代价
快是有条件的:Swoole 的常驻内存模型,意味着你写的任何全局变量、静态属性、未 unset 的大数组,都会一直留在 Worker 进程里——不是“请求结束就自动清空”了。
- 忘记
unset($hugeData)或复用协程上下文,可能引发内存缓慢增长 - DB 连接池没设超时或心跳,空闲连接可能被 MySQL 主动断开,下次取到的是失效连接
-
onWorkerStart里初始化的资源,对所有协程可见,但需确保线程/协程安全(如 Redis 连接不能跨协程共享)



















