【Cloudflare Workers 全端架構師之路 03】設定篇:KV 快取架構與分層策略
01. 前言:不要把 KV 當 Redis 用
在設計 Cloudflare Workers 架構時,最常見的錯誤就是:「我需要一個快取,那就用 KV 吧,反正它就像 Redis。」
這是一個危險的誤區。
雖然它們都是 Key-Value Store,但在分散式系統的 CAP 定理中,它們佔據完全不同的位置。
- Redis: 強調 CP 或 CA (視設定而定)。寫入快,讀取快,通常集中在單一 Region。
- Workers KV: 強調 AP (Availability & Partition Tolerance) 與 P (Performance)。讀取極快 (Read-heavy),但寫入有顯著延遲 (Write-slow),且遵循最終一致性 (Eventual Consistency)。
這一篇,我們將學習如何「正確」使用 KV,並利用它來構建一個Feature Flags (功能開關) 服務。
02. 架構比較:KV vs. Redis
為了讓你更清楚何時該用 KV,我們來看這張對比表:
| 特性 | Workers KV | Redis (如 AWS ElastiCache) |
|---|---|---|
| 部署範圍 | Global (自動複製到全球節點) | Regional (通常需手動設定 Cross-Region Replica) |
| 讀取延遲 | 極低 (從邊緣節點讀取) | 中等 (需跨越網路回到 Region) |
| 寫入速度 | 慢 (需數秒至 60 秒同步全球) | 極快 (毫秒級) |
| 一致性 | 最終一致性 (剛寫入可能讀到舊值) | 強一致性 (Strong Consistency) |
| 適用場景 | 設定檔、HTML 片段、路由規則、靜態資源 | 計數器、Session Store、即時聊天訊息 |
架構師筆記: 如果你要做「計數器 (Counter)」或「防重複提交 (Rate Limiter)」,千萬不要用 KV。因為在同步的 60 秒內,全球的節點可能會重複計算。這種場景請使用 Durable Objects (我們將在 Part 7 介紹)。
03. 實作:建立全球設定檔服務 (Feature Flags)
我們將建立一個 API,用來控制應用程式的功能開關 (例如:開啟維護模式、A/B 測試比例)。
步驟 1: 建立 Namespace
在終端機執行:
# 建立生產環境 KV
npx wrangler kv:namespace create APP_CONFIG
# 建立預覽環境 KV (避免本地開發汙染線上資料)
npx wrangler kv:namespace create APP_CONFIG --preview
複製輸出的 ID,填入 wrangler.toml:
[[kv_namespaces]]
binding = "APP_CONFIG"
id = "你的_PROD_ID"
preview_id = "你的_PREVIEW_ID"
步驟 2: 定義 TypeScript 型別
在 src/index.ts 中定義資料結構:
type Bindings = {
APP_CONFIG: KVNamespace;
}
type Config = {
maintenance_mode: boolean;
beta_feature_enabled: boolean;
promo_banner_text: string;
}
// 預設值 (Fallback)
const DEFAULT_CONFIG: Config = {
maintenance_mode: false,
beta_feature_enabled: false,
promo_banner_text: "Welcome!"
}
04. 進階實作:Tiered Caching (分層快取)
直接讀取 KV 雖然快,但 KV 的讀取操作是有免費額度限制的(且付費版也有成本)。為了極致優化,我們應該在 Worker 記憶體中再加一層快取。
Cloudflare KV 的 get 方法支援 cacheTtl 參數,這會告訴 Cloudflare:「在接下來的 N 秒內,不要去查底層資料庫,直接給我邊緣節點緩存的值。」
import { Hono } from 'hono'
const app = new Hono<{ Bindings: Bindings }>()
app.get('/config', async (c) => {
// 策略:Stale-While-Revalidate 的變體
// 設定 cacheTtl: 60 代表 60 秒內這個節點的請求都直接回傳快取
// 這能大幅降低 KV 讀取費用 (Billable Read Ops)
const configJson = await c.env.APP_CONFIG.get('global_settings', { cacheTtl: 60 });
let config: Config;
if (configJson) {
try {
config = JSON.parse(configJson);
} catch (e) {
console.error("Config parse error", e);
config = DEFAULT_CONFIG;
}
} else {
// 雖然 KV 沒資料,但為了系統穩定性,回傳預設值
config = DEFAULT_CONFIG;
}
// 加上 Browser Cache Control
// 告訴瀏覽器:你可以快取 30 秒,但在 60 秒內雖然過期了,還是可以用舊的 (stale),同時背景去更新
c.header('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
return c.json(config);
})
export default app
05. 寫入策略:如何處理最終一致性?
因為 KV 寫入慢,我們寫一個 Admin API 來更新設定。這裡要注意的是,更新後你不會立刻在 GET API 看到變更。
app.post('/admin/config', async (c) => {
const body = await c.req.json();
// 寫入 KV
// 注意:這裡沒有 cacheTtl,因為 put 操作是針對底層儲存
await c.env.APP_CONFIG.put('global_settings', JSON.stringify(body));
return c.json({
success: true,
message: "Config updated. Changes will propagate globally within 60 seconds."
});
})
架構師觀點 - 寫入延遲的應對策略:
在 UI 設計上,當管理員按下「儲存」後,不要立刻重新讀取 /config API 來刷新畫面,因為他極機率會讀到舊的。你應該直接在前端更新 UI 狀態,並顯示一個提示:「設定已儲存,正在同步至全球節點」。
06. 本地測試與驗證
- 啟動開發環境:
npm run dev - 寫入設定: 使用 Postman POST
/admin/config。 - 讀取設定: GET
/config。 - 觀察快取:
如果你在 60 秒內連續重新整理瀏覽器,你會發現 Cloudflare 的 Console Log 不會每次都跳出 KV 讀取的紀錄。這證明了
cacheTtl正在發揮作用,保護你的錢包。
07. 小結與下一步
我們今天學到了:
- KV 不是 Redis:它適合讀多寫少、對一致性要求不高的場景。
- 快取策略:利用
cacheTtl和 HTTPCache-Control來平衡即時性與成本。 - 容錯設計:永遠準備好
DEFAULT_CONFIG,防止 KV 服務異常或資料損壞。
現在我們有了設定檔,但如果我們要存取結構化的商業數據(如:使用者資料、訂單紀錄),KV 的 Key-Value 結構就顯得力不從心,而且無法做複雜查詢 (SQL)。
在下一篇 Part 4: 數據篇,我們將引入 Cloudflare 的殺手級武器 —— D1 Database (SQLite)。 我們將打破「SQLite 不適合做 Server DB」的迷思,並結合 Drizzle ORM 來展示如何優雅地處理關聯式資料。

關於作者
Ken Huang
熱衷於嘗試各類新技術的軟體開發者,現階段主力為 Android / iOS 雙平台開發,同時持續深耕前端與後端技術,以成為全端工程師與軟體架構師為目標。
最廣為人知的代表作為 BePTT。開發哲學是「以做身體健康為前提」,致力於在工作與生活平衡的基礎上,打造出擁有絕佳使用體驗的軟體服務。
這裡是用於紀錄與分享開發經驗的空間,希望能透過我的實戰筆記,幫助開發者解決疑難雜症並提升效率。
系列文章目錄
- 【Cloudflare Workers 全端架構師之路 01】觀念篇:打破雲端的物理限制
- 【Cloudflare Workers 全端架構師之路 02】入門篇:開發環境與 Hono 框架最佳實踐
- 【Cloudflare Workers 全端架構師之路 03】設定篇:KV 快取架構與分層策略 (本文)
- 【Cloudflare Workers 全端架構師之路 04】數據篇:D1 資料庫設計模式與效能優化
- 【Cloudflare Workers 全端架構師之路 05】儲存篇:R2 檔案處理與零流量費架構