快取、Redis 與 ElastiCache

寫給耀達 Lite 雲端進銷存的部署討論(2026-10-29)。接續〈資料庫擴充指南〉(db-scaling.html)第 8 節的「④ 快取」。
前提:AWS 東京、ECS Fargate 多個容器、Aurora PostgreSQL、所有店家共用資料表。

1. 先講結論

2. 快取是什麼

用雜貨店來比喻:

應用程式 「要陳記的商品清單」 快取(記憶體) 櫃台旁的小抽屜:通常不到 1 毫秒 資料庫(Aurora) 後面的倉庫:通常幾毫秒 先問這裡 抽屜沒有才走進倉庫
快取不取代資料庫,只是擋在前面,讓重複的讀取不用每次都進倉庫。

為什麼比較快

上面的毫秒數是一般常見的數量級,不是耀達 Lite 的量測結果。

兩個基本用詞

3. 最常見的用法:先看抽屜,沒有再去倉庫

這種寫法叫 cache-aside(旁路快取):快取放在旁邊,由應用程式決定什麼時候讀、什麼時候放、什麼時候刪。

要讀「陳記的商品清單」 問快取:有沒有 store:4128:products? 一把鑰匙對一份值 有(命中) 沒有(未命中) 直接用,不碰資料庫 去資料庫查 放一份進快取 設定 10 分鐘後過期(TTL) 使用 下一個人來讀,就會走左邊這條
Cache-aside:讀的時候先看快取,沒有才查資料庫並放回去。

資料改了怎麼辦:刪掉快取

老闆把「乖乖」的售價從 25 改成 28
  1. 存進資料庫(資料庫永遠是正確的那份)
  2. 刪掉快取裡的 store:4128:products
  3. 下一個人讀商品清單 → 未命中 → 從資料庫拿到 28 → 放回快取

為什麼是「刪掉」而不是「改成新值」:兩個人幾乎同時改時,「改快取」可能讓舊值蓋過新值;刪掉讓下一次從資料庫重拿,比較不會出錯。

兩個保險

4. 快取的難處

業界有一句老話:「電腦科學只有兩件難事:快取失效、幫東西取名字。」
難處會發生什麼怎麼處理
舊資料(stale) 資料庫改了,快取還是舊的。「改資料時刪快取」漏了一個地方,畫面就顯示錯的東西,而且很難查。 只放晚幾秒更新也沒關係的資料;一律設 TTL;刪快取的程式集中寫在一個地方。
大量同時過期 一大批快取在同一秒過期,所有請求一起衝向資料庫,資料庫反而被壓垮(俗稱快取雪崩)。 過期時間加一點亂數,讓它們錯開。
同一份資料大家一起重拿 某一份很熱門的快取剛過期,上百個請求同時未命中,同時去資料庫查同一件事。 只讓第一個去查,其他的等它放回快取(不少快取套件內建這個功能)。
快取本身會壞 快取服務掛了、重開了,裡面的東西全部不見。 程式寫成「快取不在時,退回直接查資料庫」。快取只能讓系統更快,不能是系統能不能動的關鍵。
多一個要顧的元件 要監控、會出事、要付錢。 對一個人 24 小時待命來說是實際的成本,所以「什麼時候加」要看數字決定(第 9 節)。

5. 快取可以放在好幾層

層放什麼耀達 Lite 的例子
瀏覽器下載過的檔案畫面的 JS、CSS
CDN(CloudFront)大家都一樣的靜態檔同上,放在離店家最近的節點
應用程式的記憶體每個容器各自存一份幾乎不會變的系統資料,例如全台銀行與分行清單
共用快取(Redis/Valkey)所有容器共用一份每家店的商品清單、商品類別、預設項目

為什麼需要「共用」那一層

耀達 Lite 會同時開好幾個容器,負載平衡器把請求輪流分給它們。如果每個容器各自在記憶體存一份:

各自存一份 容器 1 乖乖 $28(剛改) 容器 2 乖乖 $25(舊的) 容器 3 乖乖 $25(舊的) 老闆改價的請求剛好到容器 1; 店員的請求被分到容器 2,看到舊價錢。 共用一份 容器 1 容器 2 容器 3 共用快取 乖乖 $28 大家看同一份;改價時刪一次, 所有容器下一次讀都拿到新的。
容器一多,「各自存一份」就會出現同一家店在不同請求看到不同價錢。

6. Redis

Redis 是一套開源的「記憶體資料庫」:資料主要放在記憶體裡,用「鑰匙 → 值」的方式存取。它是目前最常被拿來當共用快取的軟體。

6.1 不只能存字串

結構像什麼用途例子
String一格文字或數字快取一份 JSON(某家店的商品清單)、計數器
Hash一張小表(欄位 → 值)一家店的設定:預設倉庫、預設單位⋯
List一串有順序的東西簡單的工作佇列
Set不重複的集合今天登入過的店家
Sorted Set帶分數、自動排序的集合排行榜(本月銷量前 10 名的商品)

6.2 指令一個接一個執行

Redis 處理指令時是一個接一個依序做,所以「把計數器加 1」這種單一指令不會被別人插隊。這讓它很適合做:

6.3 其他常見用途

6.4 資料會不會掉

Redis 可以設定定期把資料寫到磁碟(快照或寫操作紀錄),重開後讀回來。但當快取用時,一般當成「掉了也沒關係」:真正的資料在 Aurora,快取掉了只是慢一點,從資料庫重拿就好。

7. Redis 的授權變化與 Valkey

這段歷史會影響 AWS 上能選什麼:

對耀達 Lite 來說:用 ElastiCache 的話,授權由 AWS 處理,不用自己煩惱。Valkey 與 Redis 的指令、用法幾乎一樣,.NET 端常用的套件通常兩邊都能連(實際相容性要在選定套件時確認)。

8. ElastiCache

ElastiCache 是 AWS 代管的快取服務。就像 Aurora 是代管的 PostgreSQL,ElastiCache 是代管的 Redis/Valkey/Memcached。

8.1 代管幫你做的事

不用自己開一台機器裝 Redis、半夜處理它當機。

8.2 可以選的引擎

引擎說明
Valkey Redis 的開源分支,AWS 目前主推。比較便宜:Serverless 比 Redis OSS 便宜 33%,節點型比其他引擎便宜 20%;Serverless 最低儲存量 100MB(Redis OSS 是 1GB)。
Redis OSS原本的 Redis。
Memcached更單純,只能存字串、只當快取,沒有清單、集合、排行榜這些資料結構,也不能做第 6.2 節的鎖與限流。

8.3 兩種開法

Serverless節點型(node-based)
大小不用選,自動擴充自己選機型與台數
計費照用量(存了多少、處理多少)照開了幾台、開多久
適合剛開始、用量難估、不想管用量穩定、想精算成本

東京區的實際月費還沒有試算;ElastiCache 是很成熟的服務,東京區有提供。

9. 對耀達 Lite 的意義

9.1 放什麼、不放什麼

適合放快取(常讀、很少改、晚幾秒更新也沒關係):

不放快取,一律以資料庫為準:

9.2 多租戶一定要注意:鑰匙要帶店家編號

對:store:4128:products      ← 陳記早餐店的商品清單
對:store:7731:products      ← 好鄰居咖啡的商品清單
錯:products                 ← 所有店家共用這把鑰匙,陳記會看到好鄰居的商品

資料庫有 RLS 幫忙擋住別家的資料,快取沒有。鑰匙寫錯一次,就是一家店看到另一家店的資料,這是 SaaS 最嚴重的事故之一。 建議把「組鑰匙」寫成一個共用的函式,自動帶上目前店家的編號,不讓每個地方自己拼字串。

9.3 第一版要不要用 ElastiCache

Claude 的看法:第一版先不用。

注意:「先接記憶體版本」在多個容器時,就是第 5 節說的「各自存一份」。所以第一版放進快取的只能是幾乎不變的系統資料;每家店會改的資料(商品清單等),要等換成共用快取後才放。

10. 名詞對照

名詞白話
快取(cache)常用資料先放一份在更快的地方
命中(hit)/未命中(miss)快取裡有/沒有
命中率命中次數 ÷ 總次數
Cache-aside(旁路快取)先問快取,沒有再查資料庫並放回去
失效(invalidation)資料改了,把快取裡的舊份刪掉
TTL(存活時間)快取放多久自動過期
淘汰(eviction)、LRU記憶體滿了,丟掉最久沒用的
舊資料(stale)快取裡的值已經跟資料庫不一樣
快取雪崩大量快取同時過期,請求一起衝向資料庫
Redis最常用的開源記憶體資料庫
ValkeyLinux 基金會維護的 Redis 開源分支
Memcached只能存字串的簡單快取
ElastiCacheAWS 代管的 Valkey/Redis/Memcached
Session(登入狀態)記住「這個瀏覽器是誰、登入了沒」
限流(rate limiting)限制每段時間最多幾次請求
分散式鎖多個容器之間,確保同一件事同時只有一個在做

11. 來源

文中的毫秒數是一般數量級;耀達 Lite 的實際數字要量測。