軟體設計中的有狀態 (stateful) 與無狀態 (stateless) 的取捨
2026年6月8日
不論在前端或後端,在做軟體設計與開發時,不免會遇到要有狀態 (stateful) 還是無狀態 (stateless) 這個問題。在面試中也經常會有面試官問「XX 在設計上是有狀態的還是無狀態的? 為什麼?」,這邊的 XX 可以帶入許多不同的技術名詞,例如驗證 (auth) 上可以選有狀態的 session-based 或無狀態的 token-based,在通訊 (communication) 上可以選有狀態的 WebSockets 或無狀態的 HTTP。
要能夠回答好這個面試問題,或者在工作上做技術決策時要能選到合適的,就需要理解這兩種選擇會遇到的取捨。在這篇文章,我們會透過實際的案例,來討論兩者的區別與取捨。
從驗證的角度看有狀態與無狀態
先前我們在《登入 (驗證) 機制時常見的不同 tokens,彼此間有什麼差異? 》 有談過不同種的驗證方式,其中提到在 90 年代的網頁開發,session-based 是主流的驗證方式。session-based 的驗證方式會需要透過伺服器端來管理 session,這種作法的問題在於,今天假如網站流量變大,想要多加幾台伺服器做水平擴展,就會有 session 要如何處理的難題。
其中一種解法,是確保同一個使用者的請求都進到同個伺服器 (俗稱 sticky session 的作法);但是這種作法會讓特定使用者的狀態依賴特定伺服器。如果該伺服器掛掉,而 session 又沒有同步到共享儲存,使用者的請求雖然可以被導到別台伺服器,卻可能因為缺少原本的 session 狀態而失敗。這是一種潛在的單點故障問題 (single point of failure)。
舉例來說,在電商結帳流程,如果把商品加入購物車時,session 狀態保存在 A 伺服器,但結帳時 A 伺服器掛掉,所以請求被負載均衡器導向備援用的 B 伺服器,這時因為 B 伺服器沒有相關 session,可能導致購物車清空或直接報錯 (備註:這裡是以簡化版的購物車暫存來說明狀態綁在單台機器上的風險;實務上的電商系統通常會再把購物車狀態放到共享儲存、資料庫或快取中,避免只依賴單台伺服器記憶體)。
又或者在線上表單的填寫過程,如果要維持填寫紀錄。在填完部分資料後,狀態暫存在 A 伺服器的 Session 裡。但這時如果 A 伺服器掛掉,使用者回來繼續填寫後續步驟,送出時請求進到別台伺服器,前面填寫的資料會就此消失,讓整個流程必須從頭來過,使用體驗會很不理想。
假如我們從狀態的角度來看,上面這種 session-based 的設計,會被稱為有狀態 (stateful) 的設計。但為什麼這會被稱為有狀態?讓我們進一步來談相關的定義。
有狀態與無狀態的差別是什麼?
在軟體設計中,所謂的有狀態,是指某一個元件 (例如上述例子中的伺服器) 保有了與之互動所需的資訊 (例如上述提到的購物車中的資訊、填表暫存的資訊)。而無狀態則意味著,該元件本身不帶任何資訊,當需要用該元件時,就要帶上完整的資訊來跟該元件互動。
在日常生活中,也有類似的比喻。在早年醫療資訊還不發達時,去診所看醫生會類似於有狀態,多數人會去家裡附近的診所,而診所醫生的紙本病歷有過去所有的紀錄,甚至有些人如果比較頻繁看病,醫生會特別熟悉,下次去看病時甚至不用拿出病歷也能問診。這種狀況下,醫生的看診速度會很快;但如果今天換一家診所,新診所的醫生沒有過去的病歷,問診上可能會比較困難。
到了近代,許多醫療系統開始往電子病歷與跨院所資料交換的方向發展,這在概念上更接近把狀態從單一診所的紙本病歷,移到某種可被其他醫療單位查詢的共享系統。因此,這種做法也更像是無狀態的設計,不管在哪看診,病歷都是上傳到雲端,而不是放在診所的紙本病歷。醫生都能馬上調出過去的病歷紀錄。這樣一來,即使換診所、換醫生,新的醫生還是能根據過去的病歷來快速了解病人。
回到驗證的案例中,在《登入 (驗證) 機制時常見的不同 tokens,彼此間有什麼差異? 》我們有談到另一種 token-based 的驗證方式,是使用自包含的 token (例如 JWT)。這種設計可以讓驗證流程更接近無狀態,因為伺服器不一定要保存每個使用者的 session,只要驗證 token 的簽章與內容即可。換句話說每一個請求都要自帶足夠的資訊,讓任何一台伺服器都能獨立處理。
具體的作法來說,伺服器產生一個編碼後的 token 給客戶端,裡面已經包含驗證所需的資訊。之後每次請求都把 token 帶上,伺服器不需要保存狀態,只要驗證 token 就夠了。當狀態不是綁定在某台伺服器上,這樣即使 A 伺服器掛掉也不擔心,換成 B 伺服器一樣可行。
有狀態與無狀態的取捨
在看完上述案例討論後,讓我們回到有狀態與無狀態的取捨討論上。從上述看診與驗證的例子中,都可以看到有狀態的設計好處是速度會更快一點,但是會比較難擴展,且如果遇到事故可能導致狀態丟失。反之,無狀態的設計,則是容易擴展,且如果出事故也比較不會因單一機器故障而遺失狀態。
不過如果要從本質來看,其實所謂的無狀態,並不是真的沒有狀態,而是協定或服務節點本身不保留「必須依賴前一次請求才能理解這一次請求」的狀態;換句話說,不是無狀態,而是把狀態管理轉移。以上述的驗證來說,無狀態的 token-based 驗證,狀態其實是被轉移到客戶端持有的 token;而看診的例子來說,數位病歷是把狀態從本地的診所轉到雲端。
所以在思考有狀態與無狀態時,其實不是真的在討論有沒有狀態,而是狀態該放在哪裡、是否該在伺服器端保存。讓我們再透過另一個常見的例子,來加深對這個概念的理解。
HTTP 與 WebSocket 的狀態設計比較
在前後端的溝通中,應用層的 HTTP 是很常見的協定選擇,HTTP 是屬於無狀態的設計。HTTP 是由圖靈獎得主,同時也是全球資訊網 (WWW) 的發明者 Tim Berners-Lee 所提出的。在當時的時代背景下,硬體發展遠不及現代,伺服器用的 RAM 是 MB 量級,所以非常有限。在這種狀況下,如果把 HTTP 設計成有狀態的,伺服器端就需要保存大量客戶端狀態,記憶體就會被佔滿,不利於規模化。從全球資訊網的角度看,如果想要讓網路擴展到全球的規模,有狀態的設計將難以達成。
當把 HTTP 設計成無狀態的,對伺服器來說每次 HTTP 請求都是獨立的;每次請求處理後,伺服器就不再需要記住請求相關的資訊,因此能把空間釋放出來處理下個請求,讓請求處理能有效擴展。
HTTP 的無狀態設計能有效擴展來自客戶端的單向請求,但是如果溝通變成雙向,因為無狀態的設計,就會讓開發者必須額外找方法來處理。如果要用 HTTP 來處理雙向溝通 (例如即時通訊),因為伺服器端沒有記住狀態,所以客戶端往往必須透過輪詢 (polling) 的方式不斷發請求,過程中可能導致大量的浪費。
這也是為什麼業界後來出現了 WebSocket 這種有狀態的協定,伺服器會存下需要維持連線本身與連線相關的上下文。
WebSocket 有狀態的設計不僅減少浪費,在連線建立後,可以在同一條長連線上持續雙向傳輸資料,避免用短輪詢反覆送出 HTTP 請求所帶來的額外標頭、請求/回應往返與輪詢空轉成本。此外,在連線的狀態下,客戶端與伺服器端的資訊傳輸更快。但因為伺服器端需要管理連線的狀態,所以從擴展的角度來說,到一定量後就會遇到瓶頸。
HTTP 與 WebSocket 沒有哪一個好哪個壞,更關鍵的是適用的情境。如果是一般的客戶端請求,不在伺服器維持狀態能有效擴展,在多數的情境更適用;在伺服器維持狀態雖然限縮擴展能力,但是能減少請求的浪費與降低延遲,在需要即時且雙向通訊的情境會更適用。
閱讀更多
如果對有狀態 (stateful) 與無狀態 (stateless) 的取捨這個主題感興趣,我們在 E+ 成長計畫中的主題文有談到更多案例分析。對更深入了解這個主題,以及其他前後端開發、軟體工程、AI 工程主題感興趣的讀者,歡迎加入有超過千位工程師選擇的 E+ 一起成長 (連結)。