一句話總結:拍賣網站的快取不是「能不能做」的問題,而是「哪些能快取、快取多久、什麼時候該作廢」——搞對這三件事,頁面速度可以快 5-10 倍。
「拍賣資料每秒都在變,怎麼做快取?」這大概是我被問過最多次的問題。很多人以為拍賣網站因為有即時出價,所以整個網站都不能 cache。
錯。大錯特錯。
一個拍賣頁面上,真正「每秒在變」的只有目前最高出價跟出價次數。商品描述、賣家資訊、商品圖片、分類列表——這些東西根本不會變,或者變動頻率極低。把這些「不太動」的部分快取起來,你的網站速度馬上就有感。
快取策略是什麼?拍賣場景為什麼特別需要?
快取策略(Caching Strategy)是指決定「哪些資料要暫存、存在哪裡、存多久、什麼時候更新」的一套規則。 對拍賣網站來說,快取特別重要有三個原因:
- 流量集中在結標前:一個熱門拍品在最後 5 分鐘可能湧入幾百甚至上千人同時瀏覽。如果每次請求都打到資料庫,MySQL 會直接躺平
- 使用者對速度極度敏感:Google 的研究顯示,頁面載入時間每增加 1 秒,轉換率平均下降 7%。在拍賣場景更誇張——你慢 0.5 秒,別人就先出價了
- 重複讀取比例極高:100 個人看同一個拍品頁面,99% 的內容完全一樣,只有「目前最高出價」不同
搞懂快取策略,就等於用 10% 的伺服器成本扛住 10 倍的流量。
拍賣網站有哪些東西可以快取?
先把網站上的資料分成四個等級,從「完全靜態」到「絕對即時」:
第一級:完全靜態(快取 30 天以上)
- CSS、JavaScript 檔案(用 content hash 做檔名)
- 商品圖片(上傳後不會改)
- 字型檔案
- LOGO、favicon
這些東西直接丟 CDN,設定 Cache-Control: public, max-age=2592000, immutable,完全不需要回源。
第二級:低頻變動(快取 5-60 分鐘)
- 商品分類列表
- 賣家店鋪首頁
- 已結標的拍品頁面(結標後就不會再變了)
- 網站公告、FAQ 頁面
這些用 Redis 快取搭配 TTL,或者用 HTTP Cache 加上 stale-while-revalidate 策略。
第三級:高頻變動但可容忍短暫延遲(快取 10-60 秒)
- 進行中的拍品列表頁(新拍品上架、舊拍品結標)
- 搜尋結果頁
- 拍品的出價次數、瀏覽次數
這些資料允許 10-60 秒的延遲。用 Redis 設短 TTL,或者用 Laravel 的 Cache::remember() 就很好用。
第四級:絕對即時(不快取)
- 目前最高出價金額
- 使用者自己的出價紀錄
- 購物車 / 待付款清單
- 登入狀態、權限
這些資料透過 WebSocket 即時推送,完全不走 HTTP 快取。
Redis 在拍賣系統裡怎麼用最有效?
Redis 是拍賣網站的快取核心,但用法跟一般電商不太一樣。 以下是幾個經過實戰驗證的模式:
商品列表的分頁快取
// Laravel 範例:快取商品列表,TTL 30 秒
$items = Cache::remember(
"auction:category:{$categoryId}:page:{$page}",
30, // 秒
fn () => AuctionItem::where('category_id', $categoryId)
->where('status', 'active')
->orderBy('end_time')
->paginate(20)
);
關鍵在 cache key 的設計要包含所有查詢條件(分類、頁碼、排序),避免不同查詢打到同一個 cache。
拍品詳情頁的分層快取
一個拍品頁面的資料拆成兩塊:
- 靜態部分(商品描述、圖片、賣家資訊):快取 5 分鐘
- 動態部分(最高出價、出價次數):透過 WebSocket 即時推送,不快取
前端用 Livewire 的話,靜態部分可以用 #[Computed] 搭配 Cache::remember(),動態部分用 #[On('echo-private:...')] 監聽 Reverb 事件即時更新。這樣每次瀏覽拍品頁面,只有靜態資料的第一次請求會打到資料庫,之後都走 Redis。
熱門拍品的預熱快取
結標前 30 分鐘,系統自動把拍品詳情塞進 Redis。這個用 Laravel 的 Task Scheduling 就能做到:
// 每分鐘檢查:哪些拍品即將在 30 分鐘內結標,預載快取
Schedule::call(function () {
AuctionItem::where('end_time', '<=', now()->addMinutes(30))
->where('end_time', '>', now())
->where('cached', false)
->each(fn ($item) => $item->warmCache());
})->everyMinute();
根據我們的實測,預熱快取讓結標尖峰時段的資料庫查詢量降低了 78%,伺服器 CPU 使用率從 85% 降到 35%。
CDN 快取怎麼設定才不會出包?
CDN 處理靜態資源很簡單,但拍賣網站的 HTML 頁面要不要過 CDN 就得想清楚。
靜態資源(圖片、CSS、JS)直接丟 CDN 沒問題,設長時間快取加上 content hash 就好。但拍品頁面的 HTML 就麻煩了——因為同一個頁面,登入者跟未登入者看到的內容不一樣(登入者會看到自己的出價紀錄跟「我的最高出價」標記)。
解法:Edge Side Includes(ESI)或「骨架快取 + 動態注入」策略。
- 把拍品頁面的 HTML 骨架(不含個人化內容)快取在 CDN
- 個人化的部分用 JavaScript 在前端載入後注入
- 即時出價資料走 WebSocket,不經過 CDN
Cloudflare 的 Cache Rules 可以做到依 Cookie 決定是否走 CDN 快取。設定 Cache-Control: public, s-maxage=60 讓 CDN 快取 60 秒,搭配 stale-while-revalidate=300 讓快取過期後先回傳舊版、背景去拿新版。
這個策略實測可以讓首次載入速度加快 3-5 倍,但要注意:出價相關的 API endpoint(像 /api/bids)絕對不能被 CDN 快取,設定 Cache-Control: no-store 確保每次都打到 origin server。
Cache Invalidation 怎麼做才不會炸?
Phil Karlton 說過:「電腦科學有兩大難題——快取失效跟命名。」在拍賣場景,快取失效確實是最容易出事的地方。
幾個常見的失效情境跟處理方式:
新拍品上架
賣家上架新商品時,要作廢對應分類的列表快取:
// 上架成功後
Cache::tags(["category:{$item->category_id}"])->flush();
用 Cache Tags 可以一次清掉某個分類下所有頁碼的快取,不用一個一個刪。
拍品被下架或提前結標
除了清列表快取,拍品詳情頁的快取也要一起清:
event(new AuctionItemStatusChanged($item));
// Listener 裡面處理快取清除
Cache::forget("auction:item:{$item->id}:detail");
Cache::tags(["category:{$item->category_id}"])->flush();
用 Event + Listener 的模式可以把快取清除邏輯集中管理,不會散落在各個 Controller 裡面。如果你對拍賣資料庫設計有興趣,那篇也有談到怎麼讓資料層跟快取層配合。
防止快取雪崩
所謂快取雪崩,就是大量快取同時過期,所有請求瞬間打到資料庫。預防方式:
- TTL 加上隨機偏移:
$ttl = 300 + rand(0, 60) - 使用
Cache::lock()做快取重建鎖,同一時間只有一個 request 去重建快取 - 熱門資料永不過期,改用事件驅動主動更新
HTTP Cache Headers 該怎麼設定?
正確的 HTTP Cache Headers 是最容易被忽略、效果又最明顯的優化手段。 以下是拍賣網站各頁面建議的設定:
| 頁面類型 | Cache-Control | 備註 |
|---|---|---|
| 靜態資源 | public, max-age=31536000, immutable |
檔名含 hash |
| 已結標拍品 | public, max-age=3600 |
不會再變 |
| 進行中拍品 | private, no-cache |
走 Redis + WebSocket |
| API: 出價 | no-store |
絕對即時 |
| 商品列表 | public, s-maxage=30, stale-while-revalidate=60 |
CDN 快取 30 秒 |
stale-while-revalidate 這個 header 很適合拍賣列表頁:使用者先看到 30 秒前的版本(幾乎感覺不到差異),CDN 背景去拿新版。比起每次都等 origin server 回應,體感速度快非常多。
如果你在做SEO 優化,良好的快取策略也會直接改善 Core Web Vitals 中的 LCP(Largest Contentful Paint),因為 CDN 快取的回應時間通常在 20-50ms,比 origin server 的 200-500ms 快了一個量級。
實務上怎麼監控快取效果?
快取做完不代表做對,你需要幾個關鍵指標來驗證效果:
- Cache Hit Rate:Redis 的
INFO stats可以看到keyspace_hits跟keyspace_misses。目標至少 85% 的命中率 - CDN Hit Rate:Cloudflare 後台直接看,目標 90% 以上
- 資料庫查詢量:用 Laravel Telescope 或 Debugbar 觀察每個頁面的 SQL 查詢數。快取前一個列表頁跑 15 條 SQL,快取後應該降到 2-3 條
- P95 回應時間:用 New Relic 或 Laravel Pulse 追蹤。API endpoint 目標 P95 < 100ms,頁面目標 P95 < 500ms
如果想更進一步了解如何用數據驅動拍賣網站的優化,可以參考拍賣數據分析儀表板設計。
另外,網站效能優化通常不只是快取的事,整體的網頁設計架構也會影響載入速度。好的前端架構搭配正確的快取策略,效果才會最大化。
FAQ
Q:拍賣網站用 Redis 需要多大的記憶體? 看你的拍品數量。粗估:1 萬個活躍拍品,每個快取條目平均 2KB,列表頁快取加上去大約需要 200-500MB。Redis 的記憶體很划算,大部分場景 2GB 綽綽有餘。
Q:快取跟即時出價會不會衝突? 不會,因為它們走不同通道。即時出價走 WebSocket(例如 Laravel Reverb),快取處理的是 HTTP 請求的靜態內容。兩者各司其職,互不干擾。
Q:沒有工程團隊,能自己設定快取嗎? 靜態資源的 CDN 快取大部分 hosting 服務(如 Cloudflare)都有一鍵開啟。但 Redis 層級的應用快取需要寫程式碼,建議至少有基本的後端開發能力,或找專業的電商網站開發團隊協助。