前端與後端的快取策略比較:Redis 與 Apollo 的 key-value 應用實戰
更新日期: 2025 年 5 月 31 日
不論是打開網頁時瞬間讀取的內容,還是資料庫查詢變快的優化技巧,快取(Cache) 幾乎無所不在。
而在許多教學或系統設計中,我們常看到這樣的說法:
快取是以「key-value」的形式儲存在記憶體中。
例如常見的快取系統 Redis、或是前端的 Apollo Client,它們都選擇 key-value 結構來儲存資料。
但為什麼不是其他形式?這樣的設計有什麼優點?Redis 又和 Apollo Client 有什麼不同?
本篇文章將帶你一步步釐清這些問題。
什麼是 key-value 結構?為什麼適合做快取?
key-value 是什麼?
在程式設計與資料儲存中,key-value(鍵值對)結構 是一種非常常見的資料表示法,其核心概念是:
透過一個唯一的 key(鑰匙),對應一筆特定的資料 value(內容),用來快速存取或查找資料。
你可以把它想像成「查字典」的過程:
- 字典中的單字是
key
(例如:英文單字"apple"
) - 對應的解釋是
value
(例如:蘋果,一種水果)
在程式語言中,這樣的資料結構看起來通常是這樣:
{
"user:1": { id: 1, name: "小明", age: 18 },
"user:2": { id: 2, name: "小華", age: 20 }
}
"user:1"
是 key,用來識別這筆資料{ id: 1, name: "小明", age: 18 }
是 value,代表實際的資料內容
這種形式在不同語言中有不同的名稱與實作方式,例如:
程式語言 | 對應資料結構名稱 |
---|---|
JavaScript | Object、Map |
Python | Dictionary(dict) |
Java | HashMap |
Go | map |
Redis(伺服器) | 支援多種型別,如 String、Hash 等 key 對應結構(不是語言) |
🔔 注意:Redis 並不是一種程式語言,而是一個高效能的快取伺服器,它使用 key-value 來儲存資料,並支援多種資料型別(如 String、Hash、List、Set)。
我們在這裡只是將它列入,表示它也支援 key-value 的操作,並不是說它是語言的一種。
為什麼快取適合用 key-value?
快取(Cache)的主要目標是:
讓系統能夠在極短時間內取回常用資料,避免每次都從慢速來源(如資料庫、API)重新取得。
使用 key-value 結構來實作快取,有幾個明確的優勢:
🚀 快速查找,效能極高(O(1))
大多數 key-value 系統背後都是用 Hash Table(雜湊表) 實作。
這是一種能以幾乎「常數時間」查找資料的結構,也就是:
- 不管資料量多少,只要知道 key,就能在 O(1) 時間內取回 value。
- 比起陣列(O(n))或平衡樹(O(log n)),查詢速度極快。
這使得 key-value 結構非常適合「需要秒查資料」的場景,例如:
- 查詢使用者資料
- 顯示熱門商品清單
- 判斷是否登入、權限檢查等
🧩 結構簡單、儲存彈性高
與傳統 SQL 資料表相比,key-value 不需事先定義資料表或欄位格式,也不需要考慮 JOIN、關聯設計等複雜邏輯。
你只需要定義:
快取名稱(key) → 資料內容(value)
舉例來說:
cache["weather:Taipei"] = { temperature: 30, humidity: 70 }
這就代表你把「台北天氣資料」快取起來,之後再用 "weather:Taipei"
就能快速取出。
🧠 存取邏輯直覺,開發者容易管理
每筆資料的存取都透過清楚的 key 來標示,不需要額外學習查詢語言。這讓開發者可以直接掌控:
- 要存什麼 → 自訂 key(如
user:123
、article:456
) - 要查什麼 → 用 key 查,不需寫 SQL
這種資料管理方式,非常適合高頻讀取、動態生成內容的情境。
📌 為什麼不是用其他資料結構?
雖然也可以用陣列(Array)、JSON 結構或關聯式資料表來存資料,但在「快取」這個使用情境下:
- 速度第一
- 結構簡單
- 記憶體使用效率高
這三個條件讓 key-value 結構成為首選。
不管你用的是:
- Redis → 伺服器端快取(共用資料)
- Apollo Client → 前端快取(單一使用者)
- 自寫的記憶體快取邏輯 → 本地程式中的變數暫存
它們幾乎都是以 key-value 為核心資料結構設計的。
前端快取 vs 後端快取:架構、儲存位置與角色總覽
當我們談到「快取(Cache)」時,其實可以依照資料儲存的位置與服務的作用層級,分為兩大類:
- 前端快取(Client-side Cache)
- 後端快取(Server-side Cache)
這兩種快取都是為了提升整體效能與使用者體驗,但它們所佔用的記憶體位置、管理方式、影響範圍完全不同。
前端快取:存在使用者裝置的快取
前端快取 指的是資料儲存在使用者個人裝置上的快取,這些裝置包括:筆電、桌機、手機等。
當使用者開啟網站,載入的 JavaScript 程式就會在「本機」的資源中建立快取。
📍 儲存在哪裡?
實體對應如下:
快取方式 | 儲存媒介 | 實體對象說明 |
---|---|---|
JavaScript 變數、Apollo 快取 | 瀏覽器記憶體(RAM) | 使用者的電腦或手機記憶體,屬於揮發性 |
localStorage / sessionStorage | 瀏覽器硬碟儲存區(Persistent) | 使用者本機硬碟或 SSD |
IndexedDB | 瀏覽器內建資料庫 | 使用者硬碟空間,可儲存大型結構化資料 |
HTTP Cache(圖片、CSS、JS 等) | 磁碟快取區 | 使用者電腦的硬碟 Cache 區塊 |
🧠 什麼情況下會使用?
- 快取登入資訊、查詢結果、使用者偏好、滾動位置
- 儲存離線狀態資料(如 PWA 網頁應用)
- 搭配 Apollo Client 快取 GraphQL 查詢結果,避免重複發送請求
📌 小結:
前端快取就是在使用者自己的電腦「記」資料,速度極快,能提供流暢的 UI 體驗。但這些資料只屬於「當前使用者」,也只在「這個瀏覽器」有效。
後端快取:存在伺服器的快取
後端快取 則是指資料儲存在伺服器端的記憶體或硬碟,由網站系統或 API 所掌控,屬於跨使用者共用的快取策略。
這些快取通常部署在:
- Web Server、Application Server(應用層)
- Cache Server(如 Redis、Memcached)
- CDN 節點(邊緣節點伺服器)
📍 儲存在哪裡?
實體對應如下:
快取方式 | 儲存媒介 | 實體對象說明 |
---|---|---|
Redis / Memcached | 伺服器記憶體(RAM) | 後端主機的記憶體空間,速度快但不持久 |
程式變數快取(如全域變數) | 應用程式記憶體空間 | 運行中的應用後端服務,如 Node.js、Python |
Nginx Reverse Proxy 快取 | 伺服器硬碟或 SSD 快取目錄 | 快取 HTML、圖片等靜態頁面 |
CDN(例如 Cloudflare、Akamai) | 全球節點 RAM / SSD | 分布式網路伺服器,靠近使用者地理位置 |
🧠 什麼情況下會使用?
- 快取熱門文章、熱門商品資訊
- 快取查詢結果以降低資料庫查詢負載
- 快取圖片、CSS、JS 等靜態資源
- 快取 API 回應結果、使用者 Session 等
📌 小結:
後端快取屬於「系統共用資源」,資料通常來自資料庫查詢後的結果,可以讓多人共用、減少伺服器負擔。資料存在的地方是伺服器,不是使用者自己電腦。
前端快取 vs 後端快取:總比較
項目 | 前端快取 | 後端快取 |
---|---|---|
儲存在哪裡? | 使用者裝置(RAM / localStorage / 硬碟) | 伺服器(RAM / 硬碟 / 快取伺服器) |
控制權 | 使用者端 JavaScript 控制 | 由後端架構設計與部署者控制 |
使用對象 | 單一使用者、單一瀏覽器會話 | 多位使用者共用、跨裝置統一 |
是否可離線存取 | 視儲存機制而定(localStorage 可,記憶體不可) | 通常不可(除非搭配離線資料包) |
常見技術或工具 | Apollo Client、React Query、localStorage | Redis、Memcached、Nginx、CDN、程式記憶體快取等 |
🧩 為什麼要前後端都快取?分層快取設計的必要性
在現代 Web 應用中,使用者對「即時性」與「順暢體驗」的要求越來越高,而後端伺服器也面臨龐大的流量與資料存取壓力。這時候,如果只依賴單一層的快取機制(只有前端或只有後端),往往無法同時兼顧效能與穩定性。
這就是為什麼我們需要「分層快取(Cache Layering)」的設計概念。
🧪 實際情境流程解析
以下是一個常見情境,以「商品頁」為例:
📦 初次載入流程(Cold Start)
- 使用者打開商品頁面
- 前端(例如 React + Apollo Client)發送查詢請求
- 後端伺服器收到請求,先檢查 Redis 快取
- 如果 Redis 裡有資料(命中),就直接回傳資料,不必查資料庫
- 如果 Redis 裡沒有資料(未命中),就查詢資料庫,取得資料後:
- 將資料存入 Redis(方便下一次使用)
- 回傳資料給前端
- 前端收到資料後,寫入 Apollo Client 的快取
- 未來在同一瀏覽器中,使用者只要沒重新整理,就能從 Apollo 快取直接取得這筆資料
🔁 再次載入流程(Revisit)
使用者切換頁面、或點擊返回商品頁時:
- 前端 Apollo 會先檢查本地快取
- 如果快取中有資料,立即顯示,不需等待網路請求
- 如果快取過期或不存在,再發送請求給後端
- 後端再次收到請求 → 可能 Redis 已命中 → 快速處理
這整個流程中,同一筆資料最多只查一次資料庫,其餘都來自快取層。
🔍 每一層快取的角色與好處
快取層級 | 所在位置 | 主要目的 | 好處 |
---|---|---|---|
前端快取 | 使用者裝置(瀏覽器記憶體) | 提升單一使用者的操作體驗 | – 立即呈現資料– 減少閃爍、延遲感– 避免重複發送相同請求 |
後端快取 | 伺服器(Redis) | 減輕資料庫負載、加速回應給所有使用者 | – 減少資料庫 I/O– 同一資料可被多人共用– 整體回應速度提升 |
🧠 為什麼兩者缺一不可?
- 如果只做後端快取(例如只有 Redis):
- 每次頁面載入仍需請求伺服器 → 使用者會看到載入動畫或白畫面
- 無法提供滑順的 UI 切換體驗
- 如果只做前端快取(例如只有 Apollo):
- 首次載入或重新整理頁面時仍需查詢資料庫 → 系統承受不了高流量
- 所有使用者都要自己查一次資料 → 資料重複處理,資源浪費
✅ 前端快取優化個人操作體驗,後端快取優化整體系統效能,兩者結合才能發揮最大效益。
📌 觀念總結:不是互相替代,而是互相補強
「分層快取」的設計不是前端 vs 後端的對立,而是:
🔁 一種協同機制,讓每一層根據它「最近的位置」與「負責的範圍」去處理資料。
✅ 小結:掌握快取層級與角色,才能正確使用工具
當你理解了「誰的資料?存在誰的裝置?誰能共用?誰是即時的?」這些基本問題後,就會更自然地理解各種快取工具的設計初衷:
工具 | 所屬層級 | 核心目的 |
---|---|---|
Apollo Client | 前端快取 | 快速讀取 UI 查詢資料、避免閃爍 |
React Query | 前端快取 | 管理 REST 或 GraphQL 快取狀態 |
Redis / Memcached | 後端快取 | 加速伺服器回應、降低資料庫負擔 |
這些工具本質上都使用 key-value 結構來快取資料,只是分別發生在「使用者端」與「伺服器端」,協同合作才能打造出真正快速、穩定的應用系統。
Redis 是什麼?它在後端快取中的角色?
在我們理解了「後端快取」是儲存在伺服器端、由整個系統共用的快取層之後。
你一定會好奇:那麼,這層快取實際是由什麼工具實作的?
答案就是目前最廣泛使用、也最成熟的快取伺服器之一 —— Redis。
Redis 是什麼?一個高效能的記憶體型資料庫
Redis(Remote Dictionary Server) 是一套開源的、以記憶體為基礎的資料庫系統,專門設計用於處理高效率的資料存取與快取工作。
與傳統資料庫(如 MySQL、PostgreSQL)不同,Redis 並不是為了長期儲存而設計,而是著重於:
- 極速存取速度(毫秒等級)
- 彈性的資料快取管理
- 支援高併發的大量資料請求
這使得它非常適合被放在伺服器端應用程式的中間層 —— 介於應用伺服器與資料庫之間,用來暫時快取熱門資料,避免每次都要重新查詢資料庫。
🧠 Redis 的核心特性解析:
特性 | 說明 |
---|---|
⚡ 記憶體運作,速度極快 | 所有資料都儲存在 RAM 中,不需讀寫硬碟,因此存取速度是資料庫的數十倍以上。 |
🔑 key-value 結構為主體 | 使用類似 JavaScript 物件的方式儲存資料,例如:"user:123" → {...}。 |
🔧 支援多種資料型別 | 除了 String 外,還支援 Hash、List、Set、Sorted Set、Bitmaps、HyperLogLog 等複雜資料結構。 |
⏳ 支援自動過期與淘汰策略 | 每筆資料都可以設定 TTL(Time-To-Live),也支援 LRU/LFU 等自動清除機制。 |
🌍 可分散式部署與高可用性 | 可部署成主從架構,支援 replication、cluster 模式與自動故障轉移(failover)。 |
總結來說,Redis 就是一台「快取專用的記憶體伺服器」,為整個應用系統提供即時、可控、彈性且共用的快取層,在大多數微服務與高併發系統中扮演關鍵角色。
Redis 的應用場景與快取策略
Redis 不只是單純的 key-value 快取工具,透過其豐富的資料結構與指令設計,可以實作各種高效能服務功能:
應用場景 | 使用方式與效益說明 |
---|---|
✅ 資料庫查詢結果快取 | 將熱門查詢結果快取起來,避免同樣的查詢一再存取資料庫,有效降低 DB 負載。 |
✅ 使用者 Session 管理 | 儲存使用者登入資訊,支援跨伺服器共用登入狀態,實作高可用的登入系統。 |
✅ 排行榜 / 即時計算 | 使用 Sorted Set 結構快速統計、排序分數,常見於遊戲排名、熱銷商品排行等功能。 |
✅ 訊息佇列(Message Queue) | 搭配 List 結構可實作簡易的非同步任務排程,或使用 Pub/Sub 實作即時事件推播。 |
✅ API 結果快取(GraphQL、REST) | 將相同參數查詢的 API 結果存入 Redis,未來直接命中快取快速回應。 |
Redis 的彈性與效能讓它不只是「資料庫的副手」,而是後端系統中獨立的一層 —— 快取層(Cache Layer),你可以精確地定義哪些資料要快取、快取多久、過期後怎麼處理,並針對使用場景設計最佳的資料流。
Redis 快取資料的真實位置是?
前面說過,Redis 是部署在伺服器上的服務,那它的快取資料實際存在於哪裡?答案是:
- Redis 使用的是伺服器的 RAM(記憶體)
- 通常部署在與應用後端同一台主機上,或作為獨立快取伺服器
- 所有寫入 Redis 的資料,都會暫存在記憶體中,並依 TTL 或 LRU 策略清除
這表示:Redis 快取是屬於伺服器端資源,不會佔用使用者端的任何記憶體或儲存空間。
Redis 在「分層快取」中的角色
在前後端分層快取架構中,Redis 扮演的是:
✅ 高頻率共用資料的後端快取中心
具體貢獻包括:
- 減少資料庫壓力:讓大量使用者查詢相同資料時,只需查一次資料庫
- 提升系統整體效能:將熱門內容搬到記憶體中,回應時間大幅縮短
- 與前端快取協同運作:例如第一次請求從 Redis 取得,後續由 Apollo Client 快取接手
小結:為什麼 Redis 是後端快取的首選?
Redis 並不是單純用來「存資料」的資料庫,而是專門設計來解決「怎麼讓資料更快、可控地被多人共享使用」這件事。在大多數現代應用中,它已經成為後端架構中不可或缺的核心快取層。
只要你面對的是:
- 高流量
- 重複查詢
- 多使用者共用的動態資料
那麼,Redis 幾乎是你最佳的後端快取選擇。
Apollo Client 是什麼?它在前端快取中的角色?
在上一節,我們介紹了伺服器端的 Redis 如何作為後端快取中心,幫助整個系統承擔高併發請求、減輕資料庫壓力。
現在讓我們回到使用者端,看看在「前端快取」中,最具代表性的工具 —— Apollo Client,又扮演什麼樣的角色。
Apollo Client 是什麼?一套結合 GraphQL 的前端快取解決方案
Apollo Client 是一個專為 GraphQL API 設計的前端函式庫,廣泛應用於 React、Vue、Angular 等現代框架中。它不只是資料請求工具,最大的價值在於:
✅ 內建高階快取機制,讓前端開發者可以在無需手動處理狀態的情況下,自動管理查詢結果的存取、重用、更新與同步。
在沒有 Apollo 的情況下,開發者得自行維護 UI 狀態、管理請求 loading/error 狀態、決定資料該重取還是使用舊資料,非常繁瑣。而 Apollo 不僅讓你「拿得到資料」,更幫你「記得資料」,這就是前端快取的核心價值。
Apollo 快取的原理與結構:記憶體中的智慧資料庫
Apollo 的快取是運作在「瀏覽器的記憶體中(JavaScript heap)」,屬於前端快取的一種。
它的運作邏輯精準對應到 GraphQL 的資料模型,設計出一套以 物件型別與識別值為基礎 的 key-value 快取策略。
🚀 快取建立流程:
- 當使用者執行一段 GraphQL 查詢:
query GetUser {
user(id: 1) {
id
name
}
}
- 假設伺服器回傳:
{
"data": {
"user": {
"__typename": "User",
"id": "1",
"name": "小明"
}
}
}
- Apollo 會根據
__typename + id
建立快取 key,儲存如下:
{
"User:1": { __typename: "User", id: "1", name: "小明" }
}
- 若之後有其他查詢命中相同
User:1
,Apollo 就會直接從快取中回傳資料,完全不必再請求伺服器。
🔄 與瀏覽器記憶體的關係:
- Apollo 快取存在「瀏覽器 JavaScript 記憶體中」,屬於揮發性快取(重整就會消失)
- 每一位使用者的快取資料互不相通,僅作用於自己的瀏覽器環境
⚙️ Apollo 的快取特性與優勢
特性 | 詳細說明 |
---|---|
✅ 自動快取命中 | 查詢結果會自動快取,若 GraphQL 查詢命中已有資料,Apollo 自動回傳快取值 |
✅ 與 UI 深度整合 | 快取變動會觸發 UI 自動更新,搭配 React 時無需手動設計資料同步邏輯 |
✅ 適合個人化場景 | 適合快取當前使用者的暫存資料(如使用者資訊、查詢內容、表單暫存),不會被其他使用者共用 |
✅ 可自訂快取策略 | 提供 cache-first、cache-and-network、no-cache 等策略,開發者可依需求調整快取行為 |
✅ 支援客製化結構與合併邏輯 | 可自定義 typePolicies 決定哪些欄位該合併、哪些資料該覆蓋,適應複雜查詢場景 |
📌 Apollo 的快取行為是預設開啟的,但你可以微調其策略與結構,靈活控制資料要從快取拿、從網路拿,還是兩者並用。
使用情境:Apollo 快取解決了什麼問題?
開發場景 | 有無 Apollo 快取的差異 |
---|---|
頁面間切換時資料是否重新載入? | ✅ 有 Apollo:秒切畫面、資料直接命中快取❌ 沒 Apollo:重新請求、畫面閃爍 |
查詢相同資料是否會重複送出請求? | ✅ 有 Apollo:查一次就記住結果,避免重複查詢❌ 沒 Apollo:每次都查一次資料庫 |
使用者操作是否需等資料回來才能 render? | ✅ 有 Apollo:快取先 render,再背景更新資料❌ 沒 Apollo:畫面得等 API 回應後才顯示 |
Apollo 快取真正做到的是:為使用者創造「資料永遠在那裡」的錯覺,讓操作體驗像桌面應用一樣滑順。
Redis 與 Apollo:同樣是 key-value,分工卻不同層級
項目 | Redis(後端快取) | Apollo Client(前端快取) |
---|---|---|
儲存位置 | 伺服器記憶體(RAM) | 使用者端記憶體(瀏覽器 JavaScript Heap) |
使用對象 | 多位使用者共用 | 單一使用者、單一瀏覽器實例 |
控制權 | 後端開發者或 DevOps 團隊管理 | 前端工程師透過程式控制與設定 |
適合快取的資料類型 | 共用資料:熱門商品列表、Session 狀態、API 結果 | 私人資料:單頁查詢內容、暫存表單資料、使用者資訊等 |
快取格式與邏輯 | key-value,可支援多種複雜資料結構(如 List、Sorted Set) | key-value,以 __typename + id 為快取鍵,貼合 GraphQL 結構 |
快取協同:從 Redis 到 Apollo 的資料接力
在實務中,我們常見以下「快取接力」場景:
- 使用者開啟頁面,前端發送查詢請求
- 後端優先從 Redis 快取回應(減輕資料庫壓力)
- 回傳結果後,前端 Apollo 快取再儲存這筆資料
- 同一位使用者再次開啟該頁面時 → 直接從 Apollo 快取中命中
- 若過一段時間快取過期 → 自動重查,重新建立快取層
這種「前後端快取接力棒」的設計,就是建立現代應用最關鍵的效能優化策略:分層快取(Layered Caching)。
Redis 幫你加速整個系統,但 Apollo 才是讓使用者感受到「資料已經在那裡」的關鍵角色。
它讓 GraphQL 應用在前端實現:
- 更快的資料讀取
- 更滑順的頁面切換
- 更少的 API 請求
- 更穩定的 UI 狀態同步
✅ 結語:Redis 與 Apollo 都用 key-value,是為了極速查找與管理
無論是 Redis 還是 Apollo Client,它們都選擇 key-value 結構來快取資料,是因為這種結構擁有:
- 快速查找的能力(O(1))
- 靈活彈性的儲存方式
- 適合暫時保存與快速取得資料的應用場景
不同的是,Redis 是後端的快取伺服器,適合整個系統使用。
而 Apollo Client 是前端的快取機制,主要為單一使用者提供快速畫面回應。
兩者定位不同、使用場景也不同,但核心設計理念一致,都是為了讓資料存取更快速、更有效率。