檔期一開賣,訂單提示音才響沒幾聲,結帳頁就開始轉圈,最後跳出一頁 504。回頭看主機的監控面板,CPU 使用率卻還沒衝到 80%。多數人第一時間想到的是主機不夠力,換了更貴的方案,下一次促銷卻照樣掛掉,這正是 WooCommerce 結帳 504 最容易被誤判的地方。
真正的瓶頸往往不是硬體,而是結帳頁背後好幾層各自獨立的關卡:PHP 的工作程序、資料庫的鎖、金流閘道的回應時間、反向代理的逾時秒數,任何一關被同時湧入的結帳請求打滿,都會讓使用者等到逾時,畫面上看到的都只是同一個 504。同一批高併發的讀寫,也會在背地裡讓庫存出現超賣,訂單明明已經歸零,系統卻還讓人下單成功。
結帳頁的動態內容讓它天生無法套用整頁快取
多數網站遇到流量暴衝,第一個直覺是快取設定沒開好。整頁快取確實能救很多頁面,快取外掛或 CDN 把產生好的 HTML 存起來,下一個訪客進來直接拿現成的頁面,完全不用再跑一次 PHP 和資料庫查詢。首頁、分類頁、商品介紹頁都吃得到這個紅利,唯獨結帳頁例外。
結帳頁上顯示的購物車內容、運費、折扣、應付金額,每個訪客看到的都不一樣,本質上就不是一份能被複製貼上的 HTML。硬要套用整頁快取,訪客看到的很可能是別人的購物車或錯誤的金額,這比等待幾秒還嚴重。WooCommerce 官方開發者文件也直接把結帳頁、購物車頁、會員中心列為必須排除在頁面快取之外的頁面,理由正是這幾頁的內容會隨購物車與登入狀態變動。如果網站前面掛了 Cloudflare 這類 CDN,官方 Cache Rules 文件建議的做法是用自訂運算式辨識帶有購物車或登入 cookie 的請求,例如判斷 cookie 內容包含wordpress_logged_in這類字串,搭配 Bypass cache 這個動作讓這些請求一律略過快取,而不是乾脆把整站快取關掉。
更容易被忽略的一點是,訪客根本還沒有走到結帳頁,只是在瀏覽商品、把商品加進購物車,WooCommerce 的購物車片段(cart fragments)與 WordPress 核心的 Heartbeat 這兩支背景 AJAX 請求,就已經開始不斷呼叫admin-ajax.php,而這些請求同樣不會被快取。促銷開賣的那一刻,真正把 PHP-FPM 工作程序榨乾的,往往不是等到訪客按下送出訂單才開始,而是從第一個人打開商品頁那一刻就已經在悄悄累積。這也是為什麼 WooCommerce 結帳 504 常常在流量剛衝進來沒多久就出現,而不是等到訂單真的塞爆才發生。

先確認清楚問題出在哪一層,才不會白花力氣調錯地方。
504 出現的當下,先查這三個地方揪出卡點
504 的意思是閘道逾時,中間的反向代理伺服器(nginx、負載平衡器)等不到後端伺服器在時限內回應,只好主動切斷連線,回一頁 504 給訪客看。它不是「網站掛了」,而是「某一段沒有在時限內回話」,問題可能出在 PHP 本身很慢、資料庫被鎖住,也可能是反向代理自己的逾時秒數設得太短。多數 WooCommerce 結帳 504,真正的癥結就落在這三層之一。
以 nginx 為例,官方文件對proxy_read_timeout的定義是預設值 60 秒,計算的是跟後端伺服器兩次連續讀取動作之間的靜默時間,不是整個回應傳輸所花的總時間。換句話說,只要後端在這 60 秒之間完全沒有傳回任何資料,nginx 就會判定逾時、關閉連線並回傳 504。這個定義解釋了一個常見的誤解,很多人以為 504 代表回應太慢,但精確地說,它代表有一段時間完全沒有動靜。
排查的順序建議由裡到外,先看 PHP-FPM 的工作程序是不是被打滿,再看資料庫有沒有被鎖住,最後才懷疑是不是金流閘道自己出問題。這個順序不是隨便排的,PHP-FPM 工作程序池被占滿是最常見、也最容易當場驗證的成因;資料庫列鎖的症狀比較隱蔽,通常要交叉比對時間點才看得出來;金流閘道異常則是外部因素,不是網站這端能直接修的,放在最後確認即可,不用一開始就懷疑到那裡去。

PHP-FPM 工作程序池被打滿的徵兆與檢查指令
PHP-FPM 用一個工作程序池來同時處理進來的 PHP 請求,池子的大小由pm.max_children這個參數決定。根據 PHP 官方手冊的說明,在pm=dynamic或pm=ondemand模式下,pm.max_children就是可以同時建立的最大子行程數,這個選項是必填的。當同時湧入的結帳請求數量超過這個數字,超出的請求不會馬上被拒絕,而是先進入佇列排隊等待前面的行程處理完、釋放出空位。
問題在於,排隊本身不會出現在任何錯誤訊息裡,伺服器的錯誤紀錄不會寫著工作程序池已滿,而是等排隊時間拖過反向代理設定的逾時秒數,才會在前端變成一個 504。這也是為什麼很多人第一次遇到這個狀況會誤判成網站被攻擊或主機硬體不夠力,表面症狀完全一樣,實際成因卻是工作程序池被同一波結帳請求占滿。
要確認是不是這個原因,最直接的方法是在促銷期間查看 PHP-FPM 目前有多少子行程正在跑、其中有多少是閒置的,如果活躍的子行程數字長時間貼著pm.max_children設定的上限,新進來的請求自然只能排隊。另一個交叉比對的方式,是把反向代理記錄的 504 發生時間點,拿來對照 PHP-FPM 忙碌的時間區段,兩者若吻合,基本可以確定卡點就在這裡,而不是資料庫或金流閘道。
購物車 session 寫進資料庫造成的列鎖
如果網站沒有裝設 persistent object cache(常見的做法是用 Redis 或 Memcached),WordPress 有一部分原本該跨請求保留的快取資料,就沒辦法真的跨請求存活。WordPress 官方開發者文件對WP_Object_Cache類別的說明就寫得很明白,物件快取預設是 non-persistent,資料只存在單次請求的記憶體裡,沒有安裝 persistent 快取外掛,就不會跨頁面載入保留,代表每一次請求都可能重新去讀寫同一批底層資料。
購物車 session 正是最常被這樣處理的資料之一。WooCommerce 把 session 資料存進專屬的wp_woocommerce_sessions資料表,讀寫這張表之前會先透過物件快取查一次;沒有裝 persistent 快取,這層快取就只在單次請求內有效,等於每一次請求都要直接查詢、寫入這張資料表。促銷期間,大量訪客同時瀏覽、修改購物車內容,同一張表的同一批列被密集讀寫,InnoDB 的列鎖機制就會讓後面進來的請求排隊等待前一筆寫入完成才能繼續。這是資料庫端的卡點,跟前面 PHP-FPM 工作程序池被打滿是完全不同的層面,但在使用者眼中看到的一樣是結帳轉圈、最後跳出 504。
這也解釋了一個容易讓人摸不著頭緒的現象,明明查 PHP-FPM,子行程還有空位、沒有被占滿,結帳卻還是很慢。如果 PHP-FPM 看起來一切正常,下一步就該懷疑是不是資料庫端的列鎖在排隊,這時候真正該解的不是加開更多工作程序,而是讓 session 資料不要一直反覆寫進同一張資料表。
支付閘道的同步呼叫本身就會拖住整個結帳
伺服器資源明明還夠、資料庫也沒有出現列鎖,結帳卻還是偶爾 504,這種情況通常代表卡點出在金流閘道。WooCommerce 在訪客送出訂單的那一刻,會同步呼叫金流閘道的 API、等待授權結果回來才繼續往下走。同步的意思是,這段等待期間,負責處理這筆請求的 PHP 行程會整段卡在那裡什麼都不做,不是先把工作丟到背景處理、自己繼續接下一個請求。
一旦金流閘道自己變慢,或服務品質降級,這段等待時間就會直接轉嫁成 PHP-FPM 的占用時間拉長。原本半秒就能處理完的一個請求,可能因為閘道端塞了 3 到 5 秒授權才回來,同一段時間內能處理的請求數量因此變少,間接就吃掉更多可用的工作程序。如果剛好同時有多筆訂單都卡在等同一個變慢的閘道,很快就會把整個工作程序池填滿,連跟金流完全無關的其他結帳請求也一起被拖慢。
排查到這一步,重點不是急著改程式邏輯或升級伺服器規格,而是先確認是不是剛好遇到金流商自己的服務異常。多數金流閘道都有自己的服務狀態頁可以對照。如果閘道端本身沒有異常,問題還是持續發生,才需要回頭考慮結帳流程的其他調整。
PHP-FPM 工作程序數不是設得越大越安全
看到 504,很多人的直覺反應是把pm.max_children往上調一個很大的數字,想著工作程序越多,能同時處理的請求就越多。這個邏輯忽略了一件事,每一個 PHP-FPM 子行程都要占用一份實際的記憶體,工作程序數其實受伺服器的可用記憶體上限限制,不是想設多大就能設多大。
合理的做法是用可用記憶體除以每個工作程序平均占用的記憶體去估算這個數字,並且預留一些緩衝,而不是憑感覺填一個看起來夠大的數字。如果不管實際記憶體用量、一路把pm.max_children調高,流量尖峰時很可能不是結帳頁 504 那麼單純,而是整台伺服器因為記憶體不足而當機,連其他頁面都一起打不開,後果比 504 嚴重得多。
除了pm.max_children,PHP 官方手冊裡另一個值得留意的參數是pm.max_requests,作用是讓每個子行程處理完一定次數的請求之後就自動重啟,預設值是 0,代表無限期處理、不會自動重啟。它的用途是解決第三方函式庫可能造成的記憶體洩漏問題,如果外掛或程式庫有輕微的記憶體洩漏,長時間跑下來會慢慢侵蝕可用記憶體,設一個合理的pm.max_requests能讓子行程定期重啟、把洩漏的部分清掉。
真正該做的,是促銷開賣前先在測試環境模擬一次結帳流程,實際觀察記憶體占用情況,再回頭決定這兩個數字要設多少,而不是等到真的當機了才臨時上調,臨時調整往往是在流量尖峰、最沒有餘裕測試的時候做決定,風險反而更高。
把購物車 session 搬出資料庫,才解得掉那個列鎖
前面診斷出的資料庫列鎖問題,對應的修法是裝設 persistent object cache,常見選擇是 Redis 或 Memcached,讓 WordPress 原本該存進記憶體、卻因為沒有後端而退回寫進資料表的那部分資料,改成真正存進記憶體式的快取系統,不再反覆觸發同一張表的列更新。
這裡容易混淆的一點是,persistent object cache 跟前面談到的頁面快取是兩件完全不同的事,彼此不能互相取代。頁面快取快取的是輸出的 HTML 成品,適用在內容對每個人都一樣的頁面;物件快取快取的是資料庫查詢結果與 session 這類程式內部運作的資料,跟頁面呈現的內容無關,結帳頁這種本質上不能被頁面快取的頁面,照樣可以、也應該裝物件快取。WordPress 官方開發者文件對WP_Object_Cache的說明,前面已經提過,沒有設定 persistent 後端時,物件快取只在單次請求內存活,沒辦法跨請求保留;裝上 Redis 或 Memcached 之後,購物車 session 這類原本要反覆查詢、寫進wp_woocommerce_sessions資料表的資料,才能真正由快取系統接手處理,不再一直觸發同一批列的更新。
裝好之後怎麼確認它真的生效,是另一個常被忽略的環節。很多人只看外掛後台顯示已啟用就以為沒事了,但外掛顯示啟用不代表它真的在攔截原本該寫進資料庫的請求。比較可靠的驗證方式,是實際去檢查資料庫裡 session 相關列的數量,在裝設物件快取前後有沒有出現明顯下降,這樣才能確定它真的在發揮作用,而不是裝了一個沒在運作的外掛。
這一步的效果,是讓後面其他項優化真正發揮出來,而不是取代它們,PHP-FPM 的工作程序數還是要合理設定,購物車片段跟 Heartbeat 該精簡的還是要精簡。物件快取解決的只是資料庫這一層的排隊問題,其他層級的卡點不會因為裝了 Redis 就自動消失。
購物車片段與 Heartbeat 是兩支不同的背景請求
很多人聽說 AJAX 請求太多會拖慢結帳,就直接裝一個外掛把所有背景請求全部關掉,結果迷你購物車顯示不出商品數量,後台編輯文章時的自動儲存也跟著壞掉。會出現這種誤傷,是因為把兩支性質完全不同的背景請求混為一談了。
購物車片段是 WooCommerce 自己的機制,透過admin-ajax.php的get_refreshed_fragments動作,在不重新整理整個頁面的情況下更新迷你購物車顯示的內容。它的觸發時機不只限於結帳頁,還包括自訂事件、每 24 小時自動刷新一次、以及訪客按下瀏覽器返回按鈕的時候。WooCommerce 官方開發者部落格提到,在 7.8 版之前,購物車片段的指令碼會在網站所有頁面自動載入,即使那個頁面根本沒有用到迷你購物車這個小工具,也一樣會發出這個 AJAX 請求,等於白白堆積了不必要的請求量。官方建議的做法是透過woocommerce_get_script_data這個過濾器,只在is_woocommerce()、is_cart()、is_checkout()這幾個條件成立的頁面才載入購物車片段的腳本,其他頁面直接回傳null跳過。
Heartbeat 則是 WordPress 核心層級的機制,原本設計的用途是文章編輯時的自動儲存、鎖定提示這類跟購物車完全無關的場景,但一樣是透過admin-ajax.php跟後端保持定期連線。根據 WordPress 官方開發者文件,瀏覽器端這支心跳的間隔預設落在 15 到 120 秒之間執行一次,由伺服器端的 admin-ajax 處理程式接收資料、產生回應。官方提供的heartbeat_settings這個掛鉤可以在這個範圍內調整間隔秒數,文件裡的範例寫法是把$settings['interval']設成 60,藉此降低非必要頁面發出請求的頻率。
官方對這兩支機制建議的優化方向,都不是整站直接關閉,而是限制它們只在真正需要的頁面觸發、並拉長非必要場景的請求間隔。精準地關對地方,才不會像前面提到的那種誤傷案例,把迷你購物車跟自動儲存一起關掉,反而製造出新的問題。
反向代理層的逾時秒數要跟 PHP 對齊
就算 PHP-FPM 的工作程序數設對了、資料庫的列鎖也解掉了,還有一層很容易被漏掉,反向代理自己的逾時秒數。nginx 或 Apache 前面如果還有一層負載平衡器,每一層都各自有自己的逾時設定,沒有人特別去對齊過的話,即使後端其實有能力在合理時間內回應完成,反向代理也可能因為自己設定的秒數比較短,提早判定逾時、回傳 504。
具體來說,proxy_read_timeout,或使用 fastcgi 時對應的fastcgi_read_timeout,應該要大於等於 PHP 的max_execution_time,再加上金流閘道可能出現的最長回應時間。前面提過,nginx 官方文件對proxy_read_timeout的定義是預設值 60 秒,計算的是跟後端伺服器兩次連續讀取動作之間的逾時,而不是整個回應傳輸過程所花的時間,只要後端在這段時間內完全沒有傳輸任何內容,連線就會被關閉。如果 PHP 設定的執行逾時是 90 秒,反向代理卻只給 60 秒,即使 PHP 最終真的能在 90 秒內處理完,訪客也會先在 60 秒的時候就看到 504,PHP 那邊反而還在繼續跑一段沒人等待的請求。
調高這幾層的逾時秒數,不是解決問題的萬靈丹,只是替後面真正的優化爭取多一點反應時間。如果結帳本身要花那麼久的真正原因,像是工作程序池被占滿、資料庫列鎖、金流閘道變慢,沒有被解掉,單純調高逾時秒數只會讓訪客等更久才看到 504,對轉換率沒有幫助。這一層該做的是對齊,不是無限往上加。
快取規則沒排除結帳頁,問題會留到大檔期才爆
如果網站前面掛了 CDN 或反向代理層的快取,例如 Cloudflare,快取規則沒有正確排除購物車與結帳這幾個頁面,平常流量小的時候多半測不出問題,訪客量少,快取到錯誤內容的機率也低,問題會被流量稀釋掉。等到大檔期真正的流量湧進來,才會同時冒出結帳頁一直轉圈跟訂單金額顯示錯誤這類更難排查的怪象,因為這時候已經不只是效能問題,而是動態內容被快取到不該快取的地方。
CDN 的快取規則要用 cookie 或路徑把購物車、結帳、會員中心這幾個路徑排除掉,而且要排除的是整組行為模式,不能只排除某一個單一網址就以為結束了。Cloudflare 官方的 Cache Rules 文件示範了一種做法,用類似(http.host contains "example.com" and http.cookie contains "test-cookie")這樣的運算式,搭配 Bypass cache 這個動作,讓帶有特定 cookie,例如登入或購物車工作階段的 cookie,一律略過快取,確保交易中的訪客拿到的一定是即時內容,而不是快取版本。
規則設定完不能只看畫面上顯示規則已經套用就結束,促銷開賣前務必實際測試一次,開兩個不同的瀏覽器,或同一瀏覽器的無痕分頁,模擬兩個不同的訪客同時逛網站、放商品進購物車,確認彼此的購物車內容不會互相汙染。這跟第一節提到的結帳頁天生無法整頁快取是同一件事的延伸,但這裡講的是規則本身沒設對這個可以修正的操作面問題,不是快取機制天生的限制,換句話說,這是一個能提前測出來、也應該提前測出來的環節。
同一波結帳流量,也在背地裡製造超賣
504 不是大檔期唯一的災情。另一個常見、卻更容易被忽略的問題是超賣,後台顯示的庫存明明已經歸零,訂單卻繼續一筆一筆進來,等到出貨的時候才發現同一件商品賣出去的數量比實際庫存還多。
超賣的根本成因,跟前面講的 504 其實是同一件事的兩種不同表現,都是同一份資料,在極短時間內被多個結帳請求同時讀寫。差別在於 504 出問題的是回應時間,請求排隊排到逾時;超賣出問題的則是資料一致性,兩個幾乎同時送出的請求都讀到庫存還有 1 件,結果都被允許扣庫存,實際賣出的數量就超過庫存上限。這不是外掛設定錯了才會發生的問題,而是並發寫入本來就容易踩到的資料一致性難題。
WooCommerce 從 4.3 版開始,加入了一張專門處理這個問題的資料表,wc_reserved_stock。根據 WooCommerce 官方 GitHub 上引入這張表的 Pull Request 說明,這套機制是為了處理多位訪客同時搶購同一件庫存有限商品的並發情境,目的是避免超賣與負庫存;技術上用的是INSERT INTO ... SELECT ... FOR UPDATE這種原子性的資料庫交易操作,在讀取庫存數字的同時就把它鎖住。不是每個人都知道有這張表在背後運作,也不是每間商店都升級到有這個機制的版本,這也是不同商店回報的超賣嚴重程度會有落差的原因之一。
WooCommerce 保留庫存機制的運作方式與失守情境
保留庫存機制的原理,是在建立訂單的當下,用一次原子性的資料庫交易同時檢查並鎖定庫存數字,而不是先讀一次庫存、確認還有貨,再另外執行一次扣減的動作。傳統上容易出現超賣的寫法正是後者,先讀、再扣,中間有一段時間差,兩個幾乎同時送出的請求都可能在那段時間差裡讀到同一個還有貨的結果,實際扣減時才發現已經超賣。把讀取跟鎖定合併成同一次交易,就是為了消除這段可能出錯的時間差。
即使有這套機制,超賣的機率也不是完全歸零。WooCommerce 官方在wc_reserved_stock這個功能的開發討論裡,記錄了在傳統結帳流程、高並發壓力測試下,仍然測出負庫存最低到負 7 的情況,代表這套保留庫存機制目前還不是完全原子性、無懈可擊的解法,只是把超賣發生的機率大幅降低。

實務上的結論很直接,保留庫存機制該開著用,它確實把超賣的機率壓低了很多,但大檔期的熱門商品,尤其是庫存數字很小、搶購動作特別密集的品項,還是建議搭配人工複核,或是準備一套事後補償流程,例如超賣時主動通知消費者、提供替代方案或退款,不能完全依賴系統自動保證零超賣這件事。
區塊結帳與傳統結帳的保留庫存時間點不同
這是一個很容易被忽略、卻直接影響超賣機率的版本差異,同樣是 WooCommerce,結帳頁用的是新版的結帳區塊,還是舊版的簡碼結帳頁,保留庫存實際發生的時間點並不一樣。
根據 WooCommerce 官方 GitHub 上那份 Pull Request 的說明,使用結帳區塊時,訪客一進到結帳頁,系統就會建立一筆草稿訂單,並在這個時間點同步保留庫存,保留動作發生得比較早。使用傳統的簡碼結帳頁則不同,要等到訪客實際按下送出訂單或呼叫金流閘道進行付款,系統才會建立訂單、同時保留庫存,中間那段訪客填寫收件資料、選擇付款方式的空窗期,庫存完全沒有被鎖定,是超賣風險相對更高的一段時間。
這也解釋了一個常見的現象,同樣是 WooCommerce 商店,有些店家幾乎沒遇過超賣,有些卻經常在大檔期回報嚴重超賣,兩者的差異往往就出在結帳頁用的是哪一種版型。如果一間商店還在用傳統簡碼結帳頁,又剛好賣的是庫存極少、秒殺性質的商品,填寫資料的那段空窗期庫存沒有被鎖定,同時湧入的訪客一起搶到同一件庫存的機率自然更高。
Hold Stock 分鐘數解決的,不是全部的超賣情境
WooCommerce 後台有一個叫做保留庫存(分鐘)的設定欄位,常常被誤解成防超賣的總開關,以為只要調高這個數字,超賣問題就能一併解決。實際上它處理的是完全不同的情境。
根據 WooCommerce 官方文件對這個欄位的說明,它管的是已建立但尚未付款的訂單,當訪客下單後遲遲不付款,超過欄位設定的分鐘數,這筆待付款訂單就會被系統取消,原本保留的庫存也會釋放回可售庫存,讓其他人有機會買到。
這個設定解決的是有人下單之後卡在付款頁遲遲不付、庫存被長時間占用的問題,跟前面兩節講的兩個人幾乎同時搶最後一件庫存那種並發超賣,是完全不同層次的情境,兩者不能互相取代。設好 Hold Stock 分鐘數,不會讓並發保留機制變得更可靠;wc_reserved_stock那套機制再怎麼完善,也解決不了訂單卡在付款頁太久占用庫存的問題。實務上這個欄位如果留空,就等於停用,待付款訂單會永久占用庫存,大檔期開賣前務必確認這裡已經填了一個合理的數字。
大檔期開賣前,這些設定值要先訂出來
促銷開賣前,以下幾個設定值與檢查動作最好都先訂出來,而不是等真的當機、超賣了才臨時處理。
第一件事是把 Hold Stock 分鐘數確認並填好。官方文件說留空就是停用,大檔期前一定要填一個合理的數字,可以參考訂單常見的付款等待時間,再加一點緩衝,避免正常付款中的訂單被誤判取消,也避免遲遲不付款的訂單長時間占用熱門商品的庫存。
第二件事是評估要不要啟用 WooCommerce 官方的高效能訂單儲存,簡稱 HPOS。WooCommerce 官方文章宣布,這項架構升級已經正式穩定,並且從 8.2 版起對新安裝的商店預設啟用;官方數據指出,這項架構能讓訂單建立速度最高提升到 5 倍、結帳流程最高加快到 1.5 倍,直接降低資料庫端在流量尖峰時承受的壓力。既有商店不會自動遷移到這套架構,需要商家自行決定是否啟用,促銷前是一個值得評估的時間點。
第三件事是盤點外掛。促銷開賣前一段時間,先檢查有沒有跟結帳流程本身無關、卻會在訂單建立當下觸發一大堆背景任務的外掛,例如某些行銷或通知類外掛,會在每一筆新訂單成立時排入大量背景工作,平常流量小感覺不出差異,大檔期同時湧入大量訂單時,這些背景任務會一起搶占本來就吃緊的工作程序,值得暫停不必要的功能,把資源留給結帳本身。
第四件事是實際測試,而不是只憑理論數字下判斷。針對這次要賣的熱門商品,在正式開賣前,用測試環境模擬多個訪客同時結帳的情境,實際觀察 PHP-FPM 的占用情況與資料庫的回應時間。理論上估算出來的pm.max_children、proxy_read_timeout這些數字,只有在真的跑過一次接近實際流量的測試之後,才知道夠不夠用,這比等到促銷當天流量真的衝進來才發現設定不夠,代價要低得多。
WooCommerce 結帳 504 跟超賣看起來是兩種不同的災情,拆開來看卻是同一個根源,高併發下同一份資料被太多請求同時讀寫,某個環節撐不住,就會用不同的方式冒出來。把 PHP-FPM、資料庫、反向代理、快取規則這幾層各自對齊好,再把保留庫存機制與 Hold Stock 設定用在對的情境上,大檔期真正考驗的其實不是伺服器規格夠不夠貴,而是這幾層有沒有事先被排查過,有沒有真的模擬過一次接近實際流量的結帳情境。
