後端語言怎麼選?從併發到薪資天花板的完整分析
前言
今天讓我們選一門後端語言來開發 API,會怎麼選呢?PHP、Python、Node.js、Java、Golang——反正後端都是寫 API,那為什麼不同語言的薪資天花板差這麼多?本文將從「併發」的角度切入,帶你理解語言選擇背後的技術邏輯與產業現實!
情境思考:Foodpanda 的後端會選什麼語言?
思考一下,如果今天要開發一個像 Foodpanda、Uber Eats 這樣的外賣平台,你會選擇什麼語言來寫後端 API?
想像一下這個場景:
1 | 午餐尖峰時段 12:00~13:00: |
帶著這個問題,我們來看看每個語言的「體質」到底差在哪裡。
五大後端語言併發總覽
在深入每個語言之前,先用一張表格快速理解全貌。併發決定了語言如何處理「同時間大量請求」,這是影響系統效能最核心的因素。
先釐清兩組常被混淆的概念:
| 概念 | 定義 |
|---|---|
| 同步(Synchronous) | 呼叫方發出請求後,必須等結果回來才能繼續往下執行 |
| 異步(Asynchronous) | 呼叫方發出請求後,不需要等結果,結果透過 callback / event / Promise 通知 |
| 阻塞(Blocking) | 等待期間,Thread 被佔住,無法做其他事 |
| 非阻塞(Non-blocking) | 等待期間,Thread 不被佔住,可以去處理其他任務 |
本文後續會分別用「I/O 模型」來描述語言在 I/O 等待時的行為(阻塞/非阻塞),用「異步能力」來描述語言是否提供異步程式設計方案(async/await、callback 等),避免混用。
| 項目 | PHP | Python | Node.js | Java | Golang |
|---|---|---|---|---|---|
| 執行方式 | 直譯(8.0+ 支援 JIT) | 直譯(CPython Bytecode) | JIT 編譯(V8) | JIT 編譯(Bytecode + JVM) | 靜態編譯(AOT) |
| 型別 | 動態弱型別 | 動態強型別 | 動態弱型別 | 靜態強型別 | 靜態強型別 |
| 併發 | Process(FPM Worker) | Thread(受 GIL 限制) | Event Loop(單 Thread) | Thread | Goroutine |
| I/O 模型 | 同步阻塞(FPM) | 同步阻塞(預設)/異步非阻塞(asyncio) | 異步非阻塞(Event Loop) | 同步阻塞(預設)/異步非阻塞(NIO、CompletableFuture) | 同步寫法 + runtime 非阻塞 I/O(Goroutine 遇 I/O 自動讓出 Thread) |
| 異步程式設計 | ❌ 原生不支援(Swoole 等擴展可支援) | ✅ asyncio(async/await) | ✅ 原生(callback / Promise / async-await) | ✅ CompletableFuture、Reactive Streams | ❌ 無 async/await;透過 Goroutine + Channel 實現併發(非異步模型) |
| CPU 密集痛點 | 傳統 FPM 模式下,Process 間無法共享記憶體協作,無法多核並行處理同一任務 | GIL 限制,無法多 Thread 並行 | 單 Thread 阻塞 Event Loop | 無瓶頸,多 Thread 並行 | 無瓶頸,多核直接並行 |
| 高併發痛點 | 每個 Request 一個 Process,記憶體消耗大 | CPU 密集場景受 GIL 限制(I/O 密集可用 asyncio 緩解) | 單 Thread 處理能力有上限 | 傳統 Thread 資源重(Virtual Thread 已解決) | Goroutine 輕量,幾乎無痛點 |
| 記憶體消耗 | 20~50 MB / Process | ~8 MB / Thread(虛擬記憶體保留量,實際物理佔用較小) | 低(Event Loop 共享) | ~200 MB JVM 一次性開銷 + 每 Thread stack 512KB~1MB(虛擬記憶體保留量) | 初始 2 KB / Goroutine(動態增長) |
名詞補充:
- AOT(Ahead-of-Time Compilation,提前編譯):在程式執行前就把原始碼編譯成機器碼,執行時 CPU 直接跑,啟動快、效能穩定。例如 Golang、C。
- JIT(Just-in-Time Compilation,即時編譯):在程式執行時才動態將程式碼編譯成機器碼,可以根據實際執行狀況做最佳化,但會有啟動預熱時間。例如 Java(JVM)、Node.js(V8)。
各語言併發詳解
PHP:一個 Request 一個 Process
核心模型: PHP-FPM(FastCGI Process Manager),每個 Request 都會分配一個獨立的 Worker Process。
| 項目 | 說明 |
|---|---|
| 執行方式 | 直譯(8.0+ 支援 OPcache JIT) |
| 型別 | 動態弱型別 |
| 併發 | Process(PHP-FPM Worker,每個 Worker 獨立 Process) |
| I/O 模型 | 同步阻塞(FPM 每個 Worker 在 I/O 等待時 Thread 被佔住) |
| 異步能力 | 原生不支援;Swoole / Parallel 等擴展可提供異步或協程機制 |
| 記憶體 | 20~50 MB / Process(Laravel 框架需載入整個框架體系,更加消耗記憶體) |
PHP 的請求處理流程
1 | 【PHP-FPM 處理流程】 |
這會導致什麼問題?
1 | 假設 1 台 Server 配置 10 個 PHP-FPM Worker: |
補充:PHP 也有異步方案!
- PHP 8.0+:支援 Parallel 擴展(需 ZTS 版本 PHP,生產環境較少使用),可針對需要的部分(外部請求、CPU 密集運算等)使用多 Thread 解決阻塞
- Laravel Octane:基於 Swoole 或 RoadRunner,導入 Coroutine 機制提升併發能力
- Hyperf:基於 Swoole 的協程框架,原生支援高併發
但這些方案本質上是「PHP」追加的併發改善方案,而非語言原生支援,學習成本也較高。所以大多數企業會認為,既然這麼麻煩,一開始就選擇不會有這些瓶頸的語言,那麼就不需要考慮甚至進行大量評估了。
優點:開發速度快、生態成熟、學習門檻低
缺點:每個 Request 獨立 Process,記憶體消耗大、高併發承受力差
Python:被 GIL 鎖住的多執行緒
核心模型: 多 Thread,但受 GIL(Global Interpreter Lock)限制,同一時間只有 1 個 Thread 能執行 Python 程式碼。(⚠️ 重要:所以 Python 本質並沒有「並行處理」而是「併發處理」)
| 項目 | 說明 |
|---|---|
| 執行方式 | 直譯(CPython Bytecode) |
| 型別 | 動態強型別 |
| 併發 | Thread(受 GIL 限制);也可用 multiprocessing 開多 Process 繞過 GIL |
| I/O 模型 | 預設同步阻塞;使用 asyncio 時為異步非阻塞(I/O 密集場景表現優秀) |
| 異步能力 | asyncio(async/await),適用於 I/O 密集場景 |
| 記憶體 | ~8 MB / Thread(Linux 預設 stack 保留量,實際物理記憶體佔用較小) |
什麼是 GIL?為什麼它是瓶頸?
GIL(Global Interpreter Lock) 是 CPython 直譯器的全域鎖,它限制同一時間只能有 1 個 Thread 執行 Python 的程式碼,目的是避免 Race Condition(競爭條件)。
1 | 【GIL 的影響】 |
Python 3.13+ 的新進展:
從 Python 3.13 開始,官方提供了 Free-threaded CPython(實驗性),可以關閉 GIL,讓多 Thread 真正並行,這也是現在大多數程式語言主流的趨勢,那就是並行甚至異步處理,所以說 PHP 就是老古董玩意兒,但官方或者框架也逐漸在朝著「高併發」、「非阻塞 I/O」、「JIT」這類能讓語言效能更高的趨勢靠攏。
重要補充:GIL 主要影響 CPU 密集場景!
在 I/O 密集場景下(如 Web API、DB 查詢),GIL 會在等待 I/O 時釋放鎖,因此多 Thread 仍然有效。搭配 asyncio 更能高效處理大量 I/O 併發。此外也可以用 multiprocessing 開多 Process 來繞過 GIL 限制,實現真正的 CPU 並行。
優點:語法簡潔、AI/ML 生態系無人能敵、asyncio 處理 I/O 密集場景表現優秀
缺點:GIL 限制 CPU 密集並行能力(可用 multiprocessing 緩解)
Node.js:單執行緒的 Event Loop 魔法
核心模型: 單一 Thread 的 Event Loop + Worker Threads(需自行使用)。Event Loop 本身跑在一個 Thread 上,但 Node.js 內部的 libuv thread pool(預設 4 個 Thread)會負責處理檔案 I/O 等阻塞操作。所有 I/O 操作對開發者而言為非阻塞(Non-blocking),透過 callback / Promise 處理結果。
| 項目 | 說明 |
|---|---|
| 執行方式 | JIT 編譯(V8 引擎) |
| 型別 | 動態弱型別(主流搭配 TypeScript 成為強型別) |
| 併發 | Event Loop(單 Thread)+ Worker Threads |
| I/O 模型 | 異步非阻塞(Event Loop 處理 I/O,透過 callback / Promise 取得結果;底層 libuv 負責非阻塞 I/O) |
| 異步能力 | 原生支援(callback / Promise / async-await) |
| 記憶體 | 低(Event Loop 共享記憶體) |
Event Loop 是怎麼運作的?
1 | 【Node.js Event Loop 模型】 |
單 Thread 的致命弱點:CPU 密集任務
Node.js 官方文件明確提過:「Don’t block the Event Loop.」
當 Event Loop 在處理一個 CPU 密集任務(如 O(N²) 演算法)時,所有其他 Request 都會被擋住!
1 | 【CPU 密集任務阻塞 Event Loop】 |
解法: 使用 Worker Threads 將 CPU 密集任務移到獨立 Thread 處理:
1 | const { Worker } = require('worker_threads'); |
1 | 【單一 Node.js Process 內部的 Thread 全貌】 |
補充:Node.js 生產環境通常使用 cluster 模式!
透過 cluster 模組或 PM2,可以啟動多個 Process(通常 = CPU 核心數),每個 Process 擁有獨立的 Event Loop,藉此利用多核 CPU。所以 Node.js 並非只能用單核,只是單個 Process 內的 Event Loop 是單 Thread。
優點:I/O 密集場景效能極佳、前後端語言統一、npm 生態龐大
優點:搭配 cluster / PM2 可利用多核 CPU
缺點:單一 Event Loop 無法處理 CPU 密集任務(需 Worker Threads)、cluster 多 Process 間不共享記憶體
Java:企業級的多執行緒戰車
核心模型: 每個請求分配一個 Thread。Java 21+ 引入 Virtual Thread(虛擬執行緒),由 JVM 管理,多個 Virtual Thread 共享少量底層 Thread,大幅降低資源消耗。
| 項目 | 說明 |
|---|---|
| 執行方式 | JIT 編譯(Bytecode + JVM) |
| 型別 | 靜態強型別 |
| 併發 | Thread(Java 21+ 支援 Virtual Thread) |
| I/O 模型 | 預設同步阻塞;NIO 提供非阻塞 I/O;Virtual Thread 為同步阻塞寫法但 JVM 自動讓出底層 Thread |
| 異步能力 | CompletableFuture(異步)、Reactive Streams(異步非阻塞) |
| 記憶體 | JVM 基礎開銷 ~200 MB(Spring Boot),但屬一次性開銷 |
Java Thread 模型
1 | 【傳統 Thread(Java 20 以前)】 |
Java 21 的 Virtual Thread 是一個里程碑式的更新,讓 Java 在高併發場景下的資源消耗大幅降低,直接對標 Golang 的 Goroutine。
注意:Virtual Thread 不是異步方案,而是「同步寫法 + 非阻塞底層」!
Virtual Thread 的設計哲學跟 Goroutine 一樣——開發者寫的是同步阻塞式程式碼,但當 Virtual Thread 遇到 I/O 等待時,JVM 會自動將它從底層 OS Thread 上卸載(unmount),讓該 OS Thread 去執行其他 Virtual Thread,達到非阻塞的效果。
換句話說:
- 從呼叫方(開發者)角度看:是同步的(寫法上一行一行等結果)
- 從 Thread 資源角度看:是非阻塞的(OS Thread 不會被佔住空等)
Java 真正的異步方案是 CompletableFuture 和 Reactive Streams(Project Reactor / RxJava)——這些需要用 callback / 響應式寫法,呼叫後不等結果。Virtual Thread 則屬於併發方案,讓你用簡單的同步寫法就能享受非阻塞的好處。
優點:真正多 Thread 並行、JVM 生態成熟、靜態強型別穩定可靠
優點:Virtual Thread(Java 21+)解決了傳統 Thread 資源消耗大的問題
缺點:JVM 基礎開銷大(~200 MB)、Spring Boot 啟動慢、開發門檻較高
Golang:天生為併發而生
核心模型: Goroutine + M:N Scheduler。Go runtime 自動將輕量級的 Goroutine 分配到多個 Thread,實現真正的多核並行。
| 項目 | 說明 |
|---|---|
| 執行方式 | 靜態編譯(AOT) |
| 型別 | 靜態強型別 |
| 併發 | Goroutine(M:N Scheduler) |
| I/O 模型 | 同步寫法 + runtime 非阻塞(開發者寫同步程式碼,Goroutine 遇 I/O 時 runtime 自動讓出 Thread 給其他 Goroutine) |
| 異步能力 | 無 async/await;透過 go 關鍵字啟動 Goroutine + Channel 通訊實現併發,而非異步程式設計模型 |
| 記憶體 | 極低(初始 2 KB / Goroutine,動態增長) |
程式碼 Demo:Goroutine 併發請求外部 API
Go 沒有 async/await,但這不代表要一個一個等!用 go 關鍵字啟動 Goroutine,讓其實作併發,參考如下。
1 | package main |
1 | 執行結果: |
重點整理: 不需要 async、不需要 await、不需要 Promise,只要一個 go 關鍵字,Go runtime 就會自動幫你把 I/O 等待變成非阻塞,並在等待期間去執行其他 Goroutine 💪
Goroutine 的 M:N 調度模型
1 | 【Goroutine M:N Scheduler】 |
記憶體消耗比較
1 | 同時處理 100,000 個請求時,各語言的應對方式: |
優點:Goroutine 極輕量、原生支援無痛點、編譯為原生機器碼效能極佳
優點:語法簡潔、內建格式化工具、部署簡單(單一二進位檔)
缺點:生態系相較 Java/Python 較小、泛型支援較晚(Go 1.18+)、錯誤處理冗長
回到情境:Foodpanda 應該選什麼語言?
以 Foodpanda 這類需要高併發 + 非阻塞 I/O(避免 Thread 空等浪費資源) + CPU 密集運算的後端來說,答案已經非常明顯了。
1 | Foodpanda 後端需求分析: |
為什麼 Java / Golang 是首選?
| 需求 | PHP | Python | Node.js | Java | Golang |
|---|---|---|---|---|---|
| 高併發 | ❌ Process 太重 | ⚠️ CPU 受 GIL 限制(I/O 可用 asyncio) | ✅ Event Loop 擅長 I/O 併發 | ✅ Virtual Thread | ✅ Goroutine |
| CPU 密集 | ❌ Process 間無法協作並行 | ❌ GIL 限制並行 | ❌ 阻塞 Event Loop | ✅ 多核並行 | ✅ 多核並行 |
| I/O 模型 | ❌ 同步阻塞 | ✅ asyncio(異步非阻塞) | ✅ Event Loop(異步非阻塞) | ✅ Virtual Thread(同步寫法,底層非阻塞)/ NIO | ✅ Goroutine(同步寫法,runtime 非阻塞) |
| 系統穩定性 | ⚠️ 動態弱型別 | ⚠️ 動態型別 | ⚠️ 動態弱型別 | ✅ 靜態強型別 | ✅ 靜態強型別 |
| 運行效能 | ⚠️ 直譯(8.0+ JIT 有提升) | ❌ 直譯最慢 | ⚠️ V8 JIT 尚可 | ✅ JIT 編譯 | ✅ 原生編譯最快 |
Java 和 Golang 對於複雜的業務場景,由於本身原生就支援強大的併發能力,後續有任何需要調整的情況也能更好的應付,而不需要像 PHP 考慮是不是要導入 Swoole 一一儘管導入 Swoole 其實主要也是把 Coroutine 拿來做使用,而非完全利用 Threads。
語言選擇如何影響薪資?
記住我講的這句話(鎖定:為了避免過於混亂,這邊鎖定台灣內的所有台灣公司):
「寫 Golang 不破百都對不起自己,寫 PHP 能破百都要謝天謝地。」
那麼為什麼不同語言的薪資天花板差這麼多呢?
我認為你選擇的「語言」會決定你公司的「產業」類型,而當你的「產業」不賺錢,那麼這份職缺自然薪水不高。
舉個簡單例子:你在出版業做軟體工程師,跟在科技業做軟體工程師,同樣都輕鬆躺平弄弄日常維運,準時上下班,在累積一定年資後,後者科技產業的薪資成長性會遠遠超過出版業一一
所以台灣新聞我從來沒看過出版業薪水年終多少,幾乎都在報科技業年終多少。
獲利能力強的公司 → 用什麼語言?
第一種:客單價低但用戶量龐大
客單價低甚至免費,但擁有龐大用戶群體。因為規模化,可以做市場、賣廣告,甚至把每個免費用戶變成產品,例如 Google、Meta、Line 等。
1 | 【這類公司的技術需求】 |
語言需求:高併發 + CPU 密集 + 低延遲 → Java / Golang
薪資特點:技術門檻高、公司獲利強 → 薪資天花板高
第二種:客單價高但用戶群體小
每個客單價高,所以也有足夠的收益,例如客製化的 B2B 產品、金融產品、企業 ERP 等。
1 | 【這類公司的技術需求】 |
想像一下你花大錢買了一台蘋果手機,結果動不動就熱當機,那很顯然這個產品很快就會被淘汰。「靜態強型別」等穩健的語言自然就是首選。
語言需求:穩定可靠 + 高品質 → 靜態強型別語言
薪資特點:產品價值高、品質要求嚴格 → 薪資天花板高
那 PHP、Python、Node.js 呢?
這三個語言由於語言特性(直譯 + 動態型別),可以更快地進行開發,主要運用在需要快速迭代的場景。
| 場景 | 說明 | 常見語言 |
|---|---|---|
| MVP 快速驗證 | 先做出來搶市場,品質其次 | PHP、Node.js、Python |
| 接案公司 | to B / to G 的客製化專案 | PHP(主流) |
| 成長期企業 | 快速迭代、搶佔市場 | Node.js、Python |
| AI / ML 領域 | 模型訓練、數據分析 | Python(無可取代) |
不能說完全沒有高薪,例如在成長期的企業中快速做出 MVP,這些語言也能拿到不錯的薪資。但重點在於出成果、搶市場,品質並非最優先(不能有重大 Bug,但允許小 Bug)。
以 PHP 來說,這門語言大多常用在接案公司,可能是接 to B、to G 的案子。to C 的產品也有,但如果規模到了一定程度,就會開始逐漸重構,甚至把 API 轉移到 Golang 等語言上了。
優點:開發速度快、學習門檻低、生態豐富、轉職好上岸
缺點:產業天花板限制薪資成長性
併發比較一圖流
總結
語言選擇的核心邏輯: 語言 → 決定能力 → 決定產業 → 決定薪資天花板。
選擇語言不只是技術問題,更是職涯策略問題!(未來有機會再寫幾篇職涯策略選擇的文章!😈)
(以下只是我個人主觀的評論,僅供參考)
| 語言 | 併發 | 適合場景 | 薪資天花板(台灣) |
|---|---|---|---|
| PHP | Process(FPM) | 接案、CMS、MVP 快速開發 | ⭐⭐ |
| Node.js | Event Loop | I/O 密集 API、即時通訊、全端開發 | ⭐⭐⭐ |
| Python | Thread(GIL) | AI/ML、數據分析、腳本自動化 | ⭐⭐⭐⭐(AI 領域高薪) |
| Java | Thread / Virtual Thread | 大型企業系統、金融、電商平台 | ⭐⭐⭐⭐✨ |
| Golang | Goroutine | 高併發微服務、雲原生、基礎設施 | ⭐⭐⭐⭐⭐ |
快速決策表
| 你的情況 | 推薦語言 | 一句話理由 |
|---|---|---|
| 剛入行,想快速找到工作 | PHP / Node.js / Python | 學習門檻低、職缺多 |
| 想做 AI / 數據分析 | Python | 當前生態系難以取代的霸主 |
| 想進大型或者高流量產品公司 | Java / Golang | 高併發場景必備技能 |
| 享受躺著年資上去就能高薪 | Golang | 區塊鏈、高併發,所以有錢 |
| 想全端通吃 | Node.js + TypeScript / PHP | 前後端語言統一、包山包海 PHP |
| 已有高流量產品要重構 | Golang / Java | 穩定 + 高效能 |
延伸閱讀
各語言併發官方文件:
- PHP: Parallel - PHP 多線程擴展
- Python: asyncio - Python 異步框架
- Node.js: Event Loop - Node.js 官方 Event Loop 說明
- Java: Virtual Threads - Java 21 Virtual Thread
- Go: Goroutines - Golang 官方併發指南
相關文章:







