商品數從幾十筆膨脹到幾千筆,店家最先有感的不是前台速度,而是後台。商品列表載入要等十幾秒、批次修改價格按一次跑五分鐘、訂單清單翻頁中斷——這些症狀在商品破千前都還忍得住,破千後會以倍數放大。
很多店家第一反應是換主機、加 CPU、開更多快取外掛。這條路通常無效,因為瓶頸不在前端流量,而在後台對 wp_postmeta 這張歷史包袱資料表的大量讀寫。WooCommerce 10.x 雖然已經把訂單搬到自有資料表(HPOS),商品端仍倚賴 WordPress 原生的 postmeta 結構,超過一定規模就會出現可預期的衰退。
這篇把處理順序整理成一份 woocommerce 效能優化清單,從資料庫底層往外推到前端 CDN,照這個順序處理通常能用最低成本換到最大幅度的改善。
為什麼商品破千後後台越來越慢
問題的根本在 wp_postmeta 的結構設計。WordPress 把每個商品的價格、庫存、SKU、屬性、變體關聯等資訊全部塞進一張共用的鍵值表,一筆商品平均對應 20 至 40 列 meta,破千商品就是兩萬到四萬列起跳。後台列表只要一個篩選動作,MySQL 就得在這張表上做多次自連接(self-join),加上 meta_value 欄位預設沒有索引,整張表會被全表掃描。
商品變體會讓情況惡化。一個有 5 種尺寸、4 種顏色的商品,光是變體就佔 20 筆 post 加上對應 meta,破千 SKU 在後端常見的是 5 千到 1 萬筆 post,加上十幾萬列 postmeta。後台 /wp-admin/edit.php?post_type=product 這個頁面的查詢時間,跟 postmeta 列數呈現接近線性增長。
更隱形的成本來自背景任務。WooCommerce 的庫存同步、優惠券失效、購物車回收信都掛在 Action Scheduler 上,預設每分鐘喚醒一次。商品多了,每次喚醒要處理的任務也多,後台請求跟背景排程搶 PHP-FPM 行程,使用者就感覺到「按什麼都要等」。
換句話說,慢的不是 WordPress 也不是主機,是資料表結構碰到規模門檻。順序處理要從降低查詢成本開始,往上才是快取重複查詢、擋掉外部資源這幾層。
處理順序的四個層級
效能優化最常見的錯誤,是先裝快取外掛、再開 CDN,最後才回頭看資料庫。對破千商品的站來說順序剛好相反,越底層的優化效益越大,也越難被前面的快取掩蓋。下表把四個層級依照影響範圍與導入難度排出來,當作整篇文章的索引。
| 層級 | 影響範圍 | 預期改善 | 導入難度 | 適合對象 |
|---|---|---|---|---|
| 資料庫索引與查表結構 | 後台所有商品/訂單操作 | 商品列表載入時間從 8 至 15 秒降到 1 至 3 秒 | 中(需資料庫權限與備份) | 商品破千、後台明顯卡頓的店家 |
| 物件快取(Redis) | 前後台所有重複查詢 | wp_postmeta 查詢數可降 60% 至 80% |
中(需主機支援 Redis) | 同時有商品多與流量大的店家 |
| 圖片與靜態資源 CDN | 前台首屏載入時間 | 商品頁 LCP 從 3 至 5 秒降到 1 至 2 秒 | 低(多數主機已整合) | 商品圖多、跨區流量的店家 |
| 後台查詢與排程瓶頸 | 後台批次操作、定時任務 | 批次更新從分鐘級降到秒級 | 中至高(需理解 Action Scheduler) | 倉儲同步、會員數萬以上的店家 |
判斷邏輯很單純。沒先處理資料庫層的索引問題,後面三層的快取都只是把慢查詢結果暫存起來,快取失效或新查詢進來就立刻打回原形。預設順序由上往下走,除非站台已經確認 wp_postmeta 該補的索引都齊全,否則不要跳過第一層。
資料庫索引與查表結構
這層處理的目標只有一個——把後台對 wp_postmeta 的全表掃描變成索引查詢。WordPress 核心對這張表只建了三個基本索引(post_id、meta_key),對於 WooCommerce 的多重 meta 篩選遠遠不夠。商品破千之後,補索引、開啟 HPOS、善用 wc_product_meta_lookup 這三件事的優先序最高。
補齊 postmeta 的關鍵索引
預設的 wp_postmeta 缺一個 meta_value 前綴索引。WooCommerce 後台搜尋商品 SKU、用屬性篩選變體、按庫存狀態排序,這些動作都會用 meta_value 當條件,沒有索引就是全表掃描。實務上加一個 191 字元前綴的 meta_value 索引,再加上 post_id 與 meta_key 的複合索引,多數查詢可以快 10 至 100 倍。
操作前務必先備份。索引建立本身會鎖表,破千商品的 wp_postmeta 通常落在 50 萬到 200 萬列,在 5.7 以後的 MySQL 上會以 online DDL 處理,但仍可能阻塞寫入幾十秒,建議排在低流量時段。WordPress 外掛 Index WP MySQL For Speed 把這套索引調整包好,介面可預覽、可回滾,比手動下 SQL 更安全。
開啟 HPOS 高效訂單儲存
訂單那一側的解法是 HPOS(High-Performance Order Storage)。WooCommerce 從 8.2 開始把 HPOS 設為穩定版,新安裝預設啟用;舊站升級到 10.x 之後仍需手動切換並執行遷移。官方數據顯示啟用後訂單建立可快約 5 倍、結帳流程約 1.5 倍、單筆訂單查詢快約 40 倍,訂單越多差距越明顯。
舊站遷移有兩個地雷。一是相容性檢查,任何讀寫 wp_posts 訂單資料的舊外掛(會員回饋、發票串接、ERP 同步)都要先確認支援 HPOS。二是 sync-on-read,2026 年 4 月發布的 10.7 已將同步讀取預設關閉,遷移後若沒驗證資料一致就直接關掉相容模式,可能會丟訂單 meta。建議先在測試站完成整套訂單流程驗證,再切正式站。
善用 wc_product_meta_lookup
這張表是 WooCommerce 4.0 後內建的查詢加速層,把商品最常被篩選的欄位(價格、庫存狀態、評分、評論數、銷量、屬於哪些屬性)從 postmeta 摘出來放成扁平結構。前台的價格區間篩選、銷量排序、相關商品推薦預設都會走這張表,效能比 join postmeta 高一個量級。
實務上要注意這張表會跟 postmeta 不同步。改動商品價格、批次匯入、用 SQL 直接改資料庫都可能讓兩邊脫鉤,前台篩出來的結果就會跟商品實際狀態不一致。WooCommerce 後台「狀態 → 工具」有一個重建查詢表的選項,作為例行維護每月跑一次,或在大量匯入後立即執行。
物件快取怎麼選才不會反效果
索引處理完之後,下一層擋掉的是同一句查詢一秒鐘被打 50 次這種重複請求。物件快取(Object Cache)的作用是把 WordPress 跑出來的查詢結果放進記憶體,下次同樣的請求直接從記憶體拿。對破千商品的店家來說,主機支援 Redis 幾乎是必要條件,但設定不當會反過來拖慢站台。實作前先用下面這份指標評估配置是否到位。
- 記憶體配額:WooCommerce 中型店建議至少 512 MB Redis,商品破千加會員數千的店家拉到 1 GB 起跳。配額不足會觸發 eviction,命中率掉到 5 成以下就完全失去快取意義。
- 客戶端外掛選型:免費版 Redis Object Cache 外掛足以應付多數場景;每秒請求數破百或同時上線會員過千時,Object Cache Pro 的非同步寫入與壓縮對寫入密集的 WooCommerce 站差距明顯。
- 快取失效策略:WooCommerce 在價格、庫存、訂單變動時會主動 flush 相關 cache key,但客製外掛常忘記呼叫對應 hook,造成改了商品價格前台還是舊的這種問題,導入後第一週要在每次商品更新後抽驗前台一致性。
- 連線模式:Redis 與 PHP 之間用 Unix socket 比 TCP 快約 20% 至 30%,同主機部署優先用 socket,跨主機(外部 Redis 服務)才走 TCP,並評估網路延遲是否落在 1 ms 以內。
- 不要快取的範圍:購物車、結帳、會員後台這幾條動態路徑必須繞過任何頁面快取(Page Cache),但物件快取本身對它們仍然有效。設定時把
/cart、/checkout、/my-account放進頁面快取例外清單,物件快取維持全開。
導入順序建議先開物件快取觀察兩週,量化 wp_postmeta 查詢數的下降幅度,再決定是否升級到付費版。一開始就堆滿快取規則,反而會讓除錯複雜化。
圖片與前台 CDN 的擴充策略
商品多代表商品圖也多,每個商品配 3 至 6 張圖,加上多個尺寸自動生成,一千筆商品的圖片總量很容易破萬個檔案。前台速度的瓶頸到這個階段往往不是資料庫,而是這些圖片佔住伺服器 I/O 與頻寬。
CDN 處理的是兩件事。一是把圖片從原站搬到邊緣節點,台灣讀者連香港節點比連美國原站快上一個量級。二是格式轉換,把上傳的 JPEG/PNG 自動轉成 WebP 或 AVIF,新格式體積約少 30% 至 50%。Cloudflare、bunny.net、Cloudinary 這幾家都提供類似服務,差別在轉換是即時還是預生成、是否計算流量、能不能客製規則。
選 CDN 時要看的不是單純價格,而是計費單位。流量型計費對商品圖站不利,因為一張高解析商品圖被掃讀十次就是十次流量;請求數型計費對部落格友善,但對購物站反而合算。台灣中型店家常見的組合是 Cloudflare 免費版加自有主機 WebP 轉換外掛,月流量超過 1 TB 後再評估付費 CDN。
商品圖之外,CSS 與 JavaScript 也要進 CDN。WooCommerce 預設前台會載入 10 至 15 個資源,沒走 CDN 時這些檔案的首次連線握手就佔掉 500 ms 以上。一個簡單檢測方法是用 Chrome DevTools 的 Network 面板看 Waterfall,「Stalled」「Connecting」這兩段加起來超過 200 ms,就代表該上 CDN。
後台查詢瓶頸與 Action Scheduler 調校
走到這層,前面的索引、快取、CDN 都到位,剩下的是後台批次操作與背景排程。商品破千後的常見痛點包括批次匯入要跑 30 分鐘、優惠券失效信擠到隔天才發、會員資料同步給 ERP 一直延遲,這些都跟 Action Scheduler 的吞吐量有關。
Action Scheduler 是 WooCommerce 的背景任務系統,預設每次喚醒處理 25 個任務、單次最多 30 秒、同時最多 5 個佇列。這套參數對小店剛好,破千商品的站通常需要拉高。官方 action-scheduler-high-volume 套件把參數調成單次 60 秒、批次 50、佇列 10,吞吐量約能翻倍。WooCommerce 10.3 之後核心也已將預設 cleanup 批次從 25 拉到 50、佇列時限從 30 秒拉到 60 秒,舊站升上來後值得重新檢視自訂的調校是否還必要。
任務表本身的膨脹也要處理。wp_actionscheduler_actions 與 wp_actionscheduler_logs 在訂單量大的站很容易長到 10 至 20 GB,過大的歷史紀錄會反過來拖慢新任務的查詢。每季用 WS Action Scheduler Cleaner 或自訂 SQL 清掉 30 天前已完成的任務,配合 action_scheduler_cleanup_batch_size 過濾器把清理批次拉大,能讓任務表維持在 1 GB 以下。
最後一個常被忽略的後台慢來源是 wp_options 表的 autoload 欄位。WooCommerce 與外掛會在這張表塞進大量設定,autoload 為 yes 的列會在每次請求被讀進記憶體。商品破千的站台 autoload 資料量常常超過 5 MB,每次後台請求都多一段反序列化。用 SQL 抓出 LENGTH(option_value) > 100000 且 autoload = 'yes' 的列,逐筆評估是否真的需要 autoload,這條清理通常能再壓掉後台 200 至 500 ms 的延遲。
整套 woocommerce 效能優化做到這個層級之後,後續就是長期維護的問題。每季固定回頭量一次後台列表載入時間、Redis 命中率、Action Scheduler 任務積壓量,數字往壞的方向走超過 20%,就回到這份清單從頭檢查,比等到使用者反應才動手合算得多。