切換成本是執行緒的百分之一:Swoole 協程這本帳,算的是 PHP 高並發那條替代曲線
一篇掘金長文攤開 Swoole 協程模型的技術帳,本文從切換成本與常駐記憶體角度,拆解 PHP 高並發這條替代曲線由誰買單。
刊登 閱讀約 4 分鐘
2026 年 10 月 29 日,開發者 vipxieliang 在掘金發布一篇約十一分鐘閱讀量的長文,系統性拆解 PHP Swoole 的協程模型,涵蓋協程與執行緒、行程的差異,內部調度機制,常駐記憶體的隱患,以及與 Go goroutine 的對照。這類技術文章的價值不在新聞性,而在它把一條存在多年的替代路線,攤開成可核對的成本結構。
先看傳統模型的天花板在哪
來源文章把 PHP 傳統 FPM 模型的限制歸納為兩項。其一,一個請求對應一個行程(或一個 FPM worker),請求結束後行程內的變數、連線、記憶體全部釋放,並發能力直接受行程數上限約束,行程開多,記憶體與上下文切換成本同步上升。其二,每個請求都重新建立資料庫與 Redis 連線,熱點資料無法常駐記憶體。
換算成營運帳:連線建立與銷毀是每個請求都要付的固定開銷,QPS 愈高,這筆重複支出愈大。Swoole 的賣點就是把這筆開銷從「每請求」攤薄到「每行程生命週期」。
三層抽象的成本階梯
文章用一張對照表把行程、執行緒、協程放在同一座標上。行程由作業系統核心調度,切換涉及頁表與 TLB 刷新,成本最高,記憶體完全隔離,並發數量級在數十到數百。執行緒同樣由核心搶佔式調度,切換需進入核心態,數量級數百到數千。協程則是使用者態概念,由程式自身的執行時調度,切換只儲存少量暫存器。
來源給出的關鍵數字:協程切換成本約為執行緒切換的百分之一甚至更低,這是單機支撐十萬級並發連線的技術前提。並發數量級相應拉到數萬至數十萬。
但這份便宜有交換條件。協程是協作式調度,不會被核心隨時打斷,只在遇到讓出點時釋放控制權。Swoole 的讓出點包括網路 IO(協程化的 MySQL、Redis、HTTP 客戶端與 Co::sleep)、開啟 hook_flags 後的檔案 IO,以及顯式的 Coroutine::yield()。若一段協程代碼全是純 CPU 運算,它會佔住當前執行緒,其餘協程全部排隊。這是純 IO 密集場景與 CPU 密集場景收益差距極大的原因,前者讓出頻繁、並發優勢兌現,後者幾乎拿不到紅利。
調度結構:單執行緒事件循環裡的多協程
Swoole 的行程拓撲是 Master、Manager 到多個 Worker,每個 Worker 是一個獨立的單執行緒事件循環,協程在循環內被調度。協程遇到 IO 時掛起,控制權交還事件循環,由其驅動其他就緒協程。這個結構與 Node.js 的事件循環同源,差別在 Swoole 用協程語法把回呼攤平,開發者寫同步風格的代碼,拿到非同步的吞吐。
常駐記憶體:收益的鏡面是另一本維護帳
連線復用與資料常駐是 Swoole 的核心收益,但來源也點出代僼。行程不再按請求銷毀,意味著記憶體洩漏、全局狀態污染這類在 FPM 模型下會隨請求結束自動清零的問題,現在會跨請求累積。這與我們先前分析過的一行 console.log 引發的生產環境事故屬於同一類帳:架構把某些錯誤的自動修復機制拿掉,維護成本就從框架轉移到工程紀律。
與 Go goroutine 的對照
文章的後半段聚焦 Swoole 協程與 Go goroutine 的異同,核心差異在調度方式。Go 執行時支援搶佔式調度,CPU 密集的 goroutine 不會無限期霸佔線程;Swoole 協程依賴協作式讓出,缺少搶佔機制。這決定了兩者的適用邊界:IO 密集的業務服務兩者皆宜,CPU 密集工作負載則是 Go 的主場。
從替代曲線看,Swoole 的真實定位是給存量 PHP 團隊一條不必換語言、不必重寫業務邏輯的高並發升級路徑。它的對手不是 Go,而是「重寫」這個選項的成本。對已經把人才結構、框架生態壓在 PHP 上的團隊,Swoole 把並發上限從行程數拉到十萬級連線,已足夠支撐多數業務場景;真正需要搶佔式調度與多核並行運算的場景,遷移成本才開始劃算。
收束判斷
這篇長文可核實的部分是模型描述與成本量級,屬於作者整理的技術資料;至於個別專案的遷移效益,來源未提供實測數據,無從驗證。可確認的推論只有一條:協程切換成本壓到執行緒的百分之一量級,改變的是並發上限與單機硬體帳,而常駐記憶體把部分穩定性成本從框架轉嫁給團隊。這筆交換對 IO 密集、存量 PHP 的團隊劃算,對 CPU 密集或新建專案的團隊,Go 的調度模型仍是更完整的答案。