快取、Redis 與 ElastiCache
寫給耀達 Lite 雲端進銷存的部署討論(2026-10-29)。接續〈資料庫擴充指南〉(db-scaling.html)第 8 節的「④ 快取」。
前提:AWS 東京、ECS Fargate 多個容器、Aurora PostgreSQL、所有店家共用資料表。
1. 先講結論
- 快取:把常用、拿起來費工的資料,先放一份在更快的地方(記憶體),讓資料庫少做事。
- Redis:最常被拿來當快取的開源記憶體資料庫,用「鑰匙 → 值」存取,還有清單、集合、排行榜等資料結構。
- ElastiCache:AWS 代管的 Redis/Valkey/Memcached,不用自己裝、修補、備援。AWS 目前主推的 Valkey 是 Redis 的開源分支,價格比較低。
- 耀達 Lite:商品清單、設定這類常讀少改的資料適合放快取;庫存、金額、單據不放。快取的鑰匙一定要帶店家編號。
- Claude 的看法:第一版先不用 ElastiCache,程式先用 ASP.NET Core 的快取介面寫好,等監控看到重複讀取的壓力再加。
2. 快取是什麼
用雜貨店來比喻:
- 資料庫是後面的倉庫:東西最齊、最準,但每次都要走進去找。
- 快取是櫃台旁邊的小抽屜:只放最常賣的那幾樣,伸手就拿到。
為什麼比較快
- 資料庫查一筆資料,要解析 SQL、找索引、可能讀磁碟、確認交易,通常要幾毫秒。
- 快取把資料放在記憶體裡,用一把「鑰匙」直接拿到值,通常不到 1 毫秒。
- 更重要的是資料庫少做一件事:同一份商品清單一天被讀 10 萬次,有快取的話資料庫只要讀幾次。省下的力氣留給開單、存檔。
上面的毫秒數是一般常見的數量級,不是耀達 Lite 的量測結果。
兩個基本用詞
- 命中(hit):快取裡有,直接拿。
- 未命中(miss):快取裡沒有,只好去資料庫拿。
- 命中率:命中次數 ÷ 總次數。命中率低,代表快取放錯東西或太快過期,加了等於沒加。
3. 最常見的用法:先看抽屜,沒有再去倉庫
這種寫法叫 cache-aside(旁路快取):快取放在旁邊,由應用程式決定什麼時候讀、什麼時候放、什麼時候刪。
資料改了怎麼辦:刪掉快取
老闆把「乖乖」的售價從 25 改成 28
1. 存進資料庫(資料庫永遠是正確的那份)
2. 刪掉快取裡的 store:4128:products
3. 下一個人讀商品清單 → 未命中 → 從資料庫拿到 28 → 放回快取
為什麼是「刪掉」而不是「改成新值」:兩個人幾乎同時改時,「改快取」可能讓舊值蓋過新值;刪掉讓下一次從資料庫重拿,比較不會出錯。
兩個保險
- TTL(存活時間):每份快取都設過期時間。就算哪裡忘了刪,時間到也會自己消失,舊資料最多留這麼久。
- 淘汰(eviction):記憶體滿了,就把最久沒用的丟掉(LRU,最近最少使用)。所以快取裡的東西隨時可能不見,程式要能承受。
4. 快取的難處
業界有一句老話:「電腦科學只有兩件難事:快取失效、幫東西取名字。」
| 難處 | 會發生什麼 | 怎麼處理 |
|---|---|---|
| 舊資料(stale) | 資料庫改了,快取還是舊的。「改資料時刪快取」漏了一個地方,畫面就顯示錯的東西,而且很難查。 | 只放晚幾秒更新也沒關係的資料;一律設 TTL;刪快取的程式集中寫在一個地方。 |
| 大量同時過期 | 一大批快取在同一秒過期,所有請求一起衝向資料庫,資料庫反而被壓垮(俗稱快取雪崩)。 | 過期時間加一點亂數,讓它們錯開。 |
| 同一份資料大家一起重拿 | 某一份很熱門的快取剛過期,上百個請求同時未命中,同時去資料庫查同一件事。 | 只讓第一個去查,其他的等它放回快取(不少快取套件內建這個功能)。 |
| 快取本身會壞 | 快取服務掛了、重開了,裡面的東西全部不見。 | 程式寫成「快取不在時,退回直接查資料庫」。快取只能讓系統更快,不能是系統能不能動的關鍵。 |
| 多一個要顧的元件 | 要監控、會出事、要付錢。 | 對一個人 24 小時待命來說是實際的成本,所以「什麼時候加」要看數字決定(第 9 節)。 |
5. 快取可以放在好幾層
| 層 | 放什麼 | 耀達 Lite 的例子 |
|---|---|---|
| 瀏覽器 | 下載過的檔案 | 畫面的 JS、CSS |
| CDN(CloudFront) | 大家都一樣的靜態檔 | 同上,放在離店家最近的節點 |
| 應用程式的記憶體 | 每個容器各自存一份 | 幾乎不會變的系統資料,例如全台銀行與分行清單 |
| 共用快取(Redis/Valkey) | 所有容器共用一份 | 每家店的商品清單、商品類別、預設項目 |
為什麼需要「共用」那一層
耀達 Lite 會同時開好幾個容器,負載平衡器把請求輪流分給它們。如果每個容器各自在記憶體存一份:
6. Redis
Redis 是一套開源的「記憶體資料庫」:資料主要放在記憶體裡,用「鑰匙 → 值」的方式存取。它是目前最常被拿來當共用快取的軟體。
6.1 不只能存字串
| 結構 | 像什麼 | 用途例子 |
|---|---|---|
| String | 一格文字或數字 | 快取一份 JSON(某家店的商品清單)、計數器 |
| Hash | 一張小表(欄位 → 值) | 一家店的設定:預設倉庫、預設單位⋯ |
| List | 一串有順序的東西 | 簡單的工作佇列 |
| Set | 不重複的集合 | 今天登入過的店家 |
| Sorted Set | 帶分數、自動排序的集合 | 排行榜(本月銷量前 10 名的商品) |
6.2 指令一個接一個執行
Redis 處理指令時是一個接一個依序做,所以「把計數器加 1」這種單一指令不會被別人插隊。這讓它很適合做:
- 限流:每家店每分鐘最多 100 次請求,超過就擋。
- 分散式鎖:同一家店同一時間只能跑一次成本計價,第二個按的人會被告知「正在計算中」。容器有好幾個,鎖要放在大家都看得到的地方。
6.3 其他常見用途
- 登入狀態(session):放在 Redis,任何一個容器都認得這個人。
- 即時通知:發布/訂閱(pub/sub),例如背景的成本計價跑完,通知畫面更新。
6.4 資料會不會掉
Redis 可以設定定期把資料寫到磁碟(快照或寫操作紀錄),重開後讀回來。但當快取用時,一般當成「掉了也沒關係」:真正的資料在 Aurora,快取掉了只是慢一點,從資料庫重拿就好。
7. Redis 的授權變化與 Valkey
這段歷史會影響 AWS 上能選什麼:
- 2024 年 3 月:Redis 公司把授權從寬鬆的 BSD 改成 RSALv2/SSPLv1 雙授權,目的是不讓大型雲端業者直接拿去賣代管服務。SSPL 不被開放原始碼促進會(OSI)認可為開源授權。
- 同一個月:Linux 基金會從 Redis 7.2.4 分出 Valkey,維持 BSD 授權,AWS、Google Cloud、Oracle 等都是支持者。
- 2025 年 5 月:Redis 8 多加了 AGPLv3 作為第三種授權選項,又算是開源了。
對耀達 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 的看法:第一版先不用。
- Aurora Serverless v2 會自動加大,加上 RDS Proxy,幾百家店的讀取量應該撐得住(實際要等程式寫出來做壓力測試才知道)。
- 第一版多一個快取,就多一個會出錯、要待命的元件,還多了「舊資料」這種難查的問題。
- ASP.NET Core 有現成的快取介面
IDistributedCache:程式一開始就透過它讀寫快取,先接記憶體版本;以後換成 ElastiCache 時,只改設定、呼叫的地方不用動。 - 登入狀態可以用 cookie 或 token 放在瀏覽器端,不需要 Redis 也能多開容器。
- 等監控看到「資料庫的讀取壓力大、而且大多是重複讀同樣的東西」,再加 ElastiCache(建議 Valkey Serverless),也就是〈資料庫擴充指南〉路線圖的第二階段。
注意:「先接記憶體版本」在多個容器時,就是第 5 節說的「各自存一份」。所以第一版放進快取的只能是幾乎不變的系統資料;每家店會改的資料(商品清單等),要等換成共用快取後才放。
10. 名詞對照
| 名詞 | 白話 |
|---|---|
| 快取(cache) | 常用資料先放一份在更快的地方 |
| 命中(hit)/未命中(miss) | 快取裡有/沒有 |
| 命中率 | 命中次數 ÷ 總次數 |
| Cache-aside(旁路快取) | 先問快取,沒有再查資料庫並放回去 |
| 失效(invalidation) | 資料改了,把快取裡的舊份刪掉 |
| TTL(存活時間) | 快取放多久自動過期 |
| 淘汰(eviction)、LRU | 記憶體滿了,丟掉最久沒用的 |
| 舊資料(stale) | 快取裡的值已經跟資料庫不一樣 |
| 快取雪崩 | 大量快取同時過期,請求一起衝向資料庫 |
| Redis | 最常用的開源記憶體資料庫 |
| Valkey | Linux 基金會維護的 Redis 開源分支 |
| Memcached | 只能存字串的簡單快取 |
| ElastiCache | AWS 代管的 Valkey/Redis/Memcached |
| Session(登入狀態) | 記住「這個瀏覽器是誰、登入了沒」 |
| 限流(rate limiting) | 限制每段時間最多幾次請求 |
| 分散式鎖 | 多個容器之間,確保同一件事同時只有一個在做 |
11. 來源
- AWS:ElastiCache 與 MemoryDB 支援 Valkey(Serverless 便宜 33%、節點型便宜 20%、最低儲存 100MB)
- AWS:ElastiCache for Valkey 入門
- ElastiCache 定價
- Phoronix:Redis 8.0 加入 AGPLv3
- OneUptime:Redis 與 Valkey 分支說明(2024 年改授權、Valkey 從 7.2.4 分出、支持者)
文中的毫秒數是一般數量級;耀達 Lite 的實際數字要量測。