資料儲存技術比較:Redis、SQLite 與 IndexedDB

🌏 Read the English version


為什麼需要了解這三種資料儲存技術?

在現代應用開發中,資料儲存策略直接影響系統效能、用戶體驗和開發成本。選擇適當的儲存技術能夠:

  • 提升效能:根據存取模式選擇最佳儲存方案,降低延遲
  • 降低成本:避免過度配置伺服器資源或頻寬
  • 改善用戶體驗:實現離線功能、快速回應、流暢互動
  • 簡化架構:選對工具可減少系統複雜度

Redis、SQLite 與 IndexedDB 分別代表三種不同的儲存場景:伺服器端快取、本地端資料庫、瀏覽器端儲存。了解三者的差異與適用情境,能幫助工程師做出正確的技術決策。

三種技術的核心特性

Redis:高效能記憶體資料庫

技術背景:

Redis(Remote Dictionary Server)是開源的記憶體鍵值資料庫,以其極高的讀寫效能聞名。所有資料存放在記憶體中,支援可選的磁碟持久化機制。

核心特性:

  • 多樣資料結構:支援 String、Hash、List、Set、Sorted Set 等
  • 原子操作:所有操作都是原子性的,確保資料一致性
  • 高效能:讀寫速度可達每秒數萬至數十萬次操作
  • 持久化選項:RDB 快照與 AOF 日誌兩種機制
  • 主從複製:支援資料複製與高可用性配置
  • Pub/Sub 訊息:內建發布/訂閱機制

適用場景:

  • Session 管理:儲存用戶登入狀態
  • 快取層:減少資料庫查詢負擔
  • 排行榜:使用 Sorted Set 實現即時排名
  • 計數器:網頁瀏覽次數、API 呼叫限流
  • 即時訊息:聊天室、通知推送

限制與注意事項:

  • 記憶體成本:所有資料存於記憶體,成本較磁碟高
  • 單執行緒:雖然效能優異,但單一指令執行時間過長會阻塞其他請求
  • 資料量限制:受限於伺服器記憶體容量
  • 持久化取捨:RDB 可能遺失最近幾分鐘的資料,AOF 會影響寫入效能

SQLite:輕量級嵌入式資料庫

技術背景:

SQLite 是完全嵌入式的關聯式資料庫,資料儲存在單一檔案中,不需要獨立的資料庫伺服器。支援完整的 SQL 語法,廣泛應用於行動應用與桌面軟體。

核心特性:

  • 零配置:無需安裝或設定,直接使用
  • 完整 SQL 支援:支援 JOIN、Transaction、View、Trigger 等
  • ACID 保證:確保交易的原子性、一致性、隔離性、持久性
  • 跨平台:支援 Windows、macOS、Linux、iOS、Android
  • 小巧輕量:函式庫大小僅數百 KB
  • 可靠穩定:經過大量測試,廣泛應用於生產環境

適用場景:

  • 行動應用:iOS、Android App 的本地資料儲存
  • 桌面軟體:配置檔案、用戶資料管理
  • 嵌入式系統:IoT 裝置、車載系統
  • 原型開發:快速驗證資料模型
  • 小型網站:低流量網站的資料庫

限制與注意事項:

  • 並行寫入:同時間只允許一個寫入操作
  • 資料量限制:建議單一資料庫不超過數 GB(理論上限 281 TB)
  • 網路存取:不適合用於網路檔案系統(NFS)
  • 複雜查詢:大量 JOIN 操作效能不如專業資料庫

IndexedDB:瀏覽器端結構化儲存

技術背景:

IndexedDB 是瀏覽器提供的客戶端資料庫 API,支援儲存大量結構化資料,並提供索引功能以實現高效查詢。採用非同步 API 設計,不會阻塞主執行緒。

核心特性:

  • 大容量儲存:通常可儲存數百 MB 至數 GB 資料
  • 索引支援:可建立多個索引加速查詢
  • 交易機制:確保資料操作的一致性
  • 非同步 API:避免阻塞 UI 渲染
  • 跨分頁共享:同源的多個分頁可存取相同資料
  • 鍵值與物件儲存:支援 JavaScript 物件直接儲存

適用場景:

  • 離線優先應用:Progressive Web App (PWA) 的資料快取
  • 大型表單:暫存草稿,避免資料遺失
  • 資料同步:本地快取伺服器資料,減少網路請求
  • 多媒體內容:儲存圖片、音訊的元資料與索引
  • 遊戲狀態:瀏覽器遊戲的存檔功能

限制與注意事項:

  • 瀏覽器相容性:需檢查目標瀏覽器支援度
  • API 複雜度:使用較 localStorage 複雜,需處理回呼或 Promise
  • 安全性:受同源政策限制,但仍可能被 XSS 攻擊存取
  • 容量限制:各瀏覽器實作不同,可能在低儲存空間時被清除
  • 無 SQL 支援:查詢功能較關聯式資料庫簡單

詳細比較表格

比較項目RedisSQLiteIndexedDB
資料模型鍵值對 + 多種資料結構關聯式(表格、欄位、索引)鍵值對 + 物件儲存 + 索引
查詢語言Redis 指令集標準 SQLJavaScript API(無 SQL)
交易支援有(MULTI/EXEC)完整 ACID 交易有(Transaction API)
持久化可選(RDB、AOF)預設持久化至檔案預設持久化至瀏覽器儲存
並行處理單執行緒 + 多工處理多讀單寫依瀏覽器實作
典型容量數 GB(記憶體限制)數 GB ~ 數十 GB50MB ~ 數 GB(依瀏覽器)
讀取效能極高(微秒等級)高(毫秒等級)中等(依資料量與索引)
寫入效能極高中等(受鎖定機制影響)中等
資料安全性網路傳輸需加密檔案權限保護同源政策隔離
部署複雜度需獨立伺服器無需部署(嵌入式)無需部署(瀏覽器內建)
開發難度中等低(熟悉 SQL 即可)中高(API 較複雜)
維護成本需監控與調校幾乎無維護成本無維護成本

實務應用案例

案例一:電商網站的 Session 管理(Redis)

需求:
電商平台需要管理數萬個同時在線用戶的 Session,包含購物車、登入狀態、瀏覽紀錄等。

為何選擇 Redis:

  • 高並發讀寫能力,應對促銷活動的流量尖峰
  • 支援 Session 自動過期(TTL 機制)
  • 主從複製確保高可用性
  • 跨伺服器共享 Session,支援水平擴展

實作重點:

# 設定 Session(30 分鐘過期)
SET session:user123 "{"cart":[1,2,3],"logged_in":true}" EX 1800

# 讀取 Session
GET session:user123

# 延長 Session 時效
EXPIRE session:user123 1800

案例二:筆記 App 的離線儲存(SQLite)

需求:
行動筆記應用需要在離線狀態下正常運作,儲存筆記內容、分類、標籤、附件等結構化資料。

為何選擇 SQLite:

  • 完整的關聯式資料庫功能,支援複雜查詢
  • ACID 保證,確保資料不會因 App 崩潰而損毀
  • 跨平台支援,iOS 與 Android 共用相同程式碼
  • 無需網路連線,完全本地運作

實作重點:

-- 建立筆記表
CREATE TABLE notes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  content TEXT,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 建立標籤表
CREATE TABLE tags (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT UNIQUE NOT NULL
);

-- 建立筆記-標籤關聯表
CREATE TABLE note_tags (
  note_id INTEGER,
  tag_id INTEGER,
  FOREIGN KEY (note_id) REFERENCES notes(id),
  FOREIGN KEY (tag_id) REFERENCES tags(id),
  PRIMARY KEY (note_id, tag_id)
);

-- 查詢包含特定標籤的筆記
SELECT n.* FROM notes n
JOIN note_tags nt ON n.id = nt.note_id
JOIN tags t ON nt.tag_id = t.id
WHERE t.name = '工作';

案例三:PWA 離線地圖應用(IndexedDB)

需求:
Progressive Web App 地圖服務需要快取地圖圖磚、地點資訊、使用者標記,實現離線瀏覽功能。

為何選擇 IndexedDB:

  • 可儲存大量二進制資料(地圖圖片)
  • 索引功能支援快速查詢特定地理範圍的資料
  • 非同步 API 不影響地圖互動流暢度
  • 無需伺服器端支援,完全在瀏覽器運作

實作重點:

// 開啟資料庫
const request = indexedDB.open('MapDatabase', 1);

request.onupgradeneeded = function(event) {
  const db = event.target.result;
  
  // 建立圖磚儲存區
  const tileStore = db.createObjectStore('tiles', { keyPath: 'id' });
  tileStore.createIndex('zoom', 'zoom', { unique: false });
  tileStore.createIndex('coordinates', ['x', 'y'], { unique: false });
  
  // 建立地點儲存區
  const placeStore = db.createObjectStore('places', { keyPath: 'id' });
  placeStore.createIndex('category', 'category', { unique: false });
};

// 儲存地圖圖磚
function saveTile(db, tile) {
  const transaction = db.transaction(['tiles'], 'readwrite');
  const store = transaction.objectStore('tiles');
  store.put(tile);
}

// 查詢特定縮放等級的圖磚
function getTilesByZoom(db, zoom) {
  return new Promise((resolve, reject) => {
    const transaction = db.transaction(['tiles'], 'readonly');
    const store = transaction.objectStore('tiles');
    const index = store.index('zoom');
    const request = index.getAll(zoom);
    
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
  });
}

選擇決策流程圖

快速決策步驟:

  1. 確認運行環境
    • 伺服器端 → 考慮 Redis 或其他伺服器資料庫
    • 客戶端應用(App)→ 考慮 SQLite
    • 瀏覽器端 → 考慮 IndexedDB
  2. 評估資料特性
    • 需要超高速讀寫 → Redis
    • 需要複雜 SQL 查詢 → SQLite
    • 需要大容量瀏覽器儲存 → IndexedDB
  3. 考量資料持久性
    • 臨時資料、快取 → Redis
    • 長期儲存、關鍵資料 → SQLite 或後端資料庫
    • 離線優先應用 → IndexedDB
  4. 預估資料量與流量
    • 高並發、小資料量 → Redis
    • 中等流量、結構化資料 → SQLite
    • 前端資料快取 → IndexedDB

最佳實踐建議

Redis 最佳實踐

  • 設定適當的過期時間:避免記憶體浪費,使用 EXPIRE 或 SETEX 指令
  • 監控記憶體使用:設定 maxmemory 與淘汰策略(如 allkeys-lru)
  • 避免大 key:單一鍵值不應超過 10MB,考慮拆分資料
  • 使用 Pipeline:批次操作減少網路往返次數
  • 正確選擇資料結構:例如計數器用 String,排行榜用 Sorted Set
  • 啟用持久化:生產環境建議同時使用 RDB 與 AOF
  • 主從複製:至少配置一個從節點,確保高可用性

SQLite 最佳實踐

  • 使用 WAL 模式:提升並發讀取效能(PRAGMA journal_mode=WAL)
  • 建立適當索引:加速常用查詢,但避免過多索引影響寫入
  • 使用交易:批次寫入時使用 BEGIN/COMMIT 包裹,提升效能
  • 定期 VACUUM:清理碎片,回收空間
  • 避免在 NFS 使用:網路檔案系統可能導致檔案鎖定問題
  • 備份策略:定期複製資料庫檔案或使用 .backup 指令
  • 限制資料庫大小:單一資料庫建議不超過數 GB

IndexedDB 最佳實踐

  • 使用 Promise 包裝:簡化非同步操作,可使用 idb 函式庫
  • 建立適當索引:加速查詢,但每個索引都會增加儲存空間
  • 處理版本升級:onupgradeneeded 中謹慎處理結構變更
  • 錯誤處理:始終處理 onerror 與 onblocked 事件
  • 批次操作:使用單一交易處理多筆資料,提升效能
  • 清理過期資料:定期刪除不需要的快取資料
  • 檢查瀏覽器支援:使用 feature detection 確保相容性
  • 考慮容量限制:檢查可用空間,處理 QuotaExceededError

常見問題 FAQ

Q1: Redis 適合當作主要資料庫使用嗎?

A: 通常不建議。Redis 主要設計為快取與高速資料存取層,雖然支援持久化,但以下原因使其較不適合作為主資料庫:

  • 記憶體成本高,儲存大量資料不經濟
  • 持久化機制有資料遺失風險(RDB 可能遺失數分鐘資料)
  • 缺乏複雜查詢能力(無 JOIN、無完整 SQL)
  • 單執行緒架構在某些場景可能成為瓶頸

建議搭配傳統關聯式或 NoSQL 資料庫使用,Redis 作為快取層或特定功能(如排行榜、Session)的儲存。

Q2: SQLite 能承受多大的並發量?

A: SQLite 採用檔案鎖定機制,並發寫入能力有限:

  • 讀取:支援多個程序同時讀取
  • 寫入:同時間只允許一個寫入操作
  • WAL 模式:讀寫可同時進行,但寫寫仍互斥

適用場景建議:

  • ✅ 讀多寫少的應用(如內容管理系統)
  • ✅ 單用戶應用(桌面軟體、行動 App)
  • ✅ 低流量網站(每日數千至數萬次請求)
  • ❌ 高並發寫入(建議使用 MySQL、PostgreSQL)
  • ❌ 多伺服器環境(需要集中式資料庫)

Q3: IndexedDB 的資料會被瀏覽器清除嗎?

A: 可能會,取決於以下因素:

  • 儲存空間不足:瀏覽器可能清除 IndexedDB 資料以釋放空間
  • 用戶操作:清除瀏覽資料時會一併刪除 IndexedDB
  • 持久化儲存 API:可要求瀏覽器不自動清除資料

建議做法:

// 請求持久化儲存權限
if (navigator.storage && navigator.storage.persist) {
  navigator.storage.persist().then(granted => {
    if (granted) {
      console.log('資料將不會被自動清除');
    } else {
      console.log('瀏覽器可能在空間不足時清除資料');
    }
  });
}

// 檢查目前儲存狀態
navigator.storage.persisted().then(isPersisted => {
  console.log('持久化狀態:', isPersisted);
});

Q4: 如何在 Redis、SQLite 與 IndexedDB 之間選擇?

A: 根據應用場景快速決策:

需求建議技術理由
Session 管理、API 限流Redis高並發、支援過期時間
行動 App 本地資料SQLite完整 SQL、ACID 保證
PWA 離線功能IndexedDB大容量、瀏覽器原生支援
即時排行榜RedisSorted Set 高效排序
嵌入式系統資料庫SQLite輕量級、無需伺服器
大型表單暫存IndexedDB避免伺服器頻繁請求
分散式快取Redis支援主從複製、Cluster

Q5: 可以同時使用多種儲存技術嗎?

A: 可以,且這是常見的最佳實踐。混合使用範例:

  • 電商平台
    • Redis:Session、購物車、熱門商品快取
    • MySQL:訂單、商品、用戶主資料
    • IndexedDB:用戶瀏覽紀錄、草稿
  • 筆記應用
    • SQLite:App 本地資料庫
    • PostgreSQL:雲端同步資料
    • Redis:即時協作狀態
  • 地圖服務
    • IndexedDB:地圖圖磚快取
    • Redis:即時路況資料
    • PostgreSQL + PostGIS:地理資料主庫

關鍵是根據資料特性(讀寫頻率、資料量、持久性需求)選擇合適的技術。

Q6: Redis 的記憶體資料遺失時會怎樣?

A: 影響取決於持久化配置:

  • 無持久化:重啟後所有資料消失,僅適合純快取場景
  • RDB 快照:恢復到最後一次快照時間點,可能遺失數分鐘至數小時資料
  • AOF 日誌
    • appendfsync always:幾乎不遺失,但效能最差
    • appendfsync everysec:最多遺失1秒資料(建議設定)
    • appendfsync no:由作業系統決定,可能遺失較多資料
  • RDB + AOF:結合兩者優點,恢復時優先使用 AOF

生產環境建議:

# redis.conf 設定
save 900 1          # 900 秒內至少 1 次變更則快照
save 300 10         # 300 秒內至少 10 次變更則快照
save 60 10000       # 60 秒內至少 10000 次變更則快照

appendonly yes                # 啟用 AOF
appendfsync everysec          # 每秒同步一次
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

Q7: IndexedDB 與 localStorage 有什麼差別?

A: 兩者都是瀏覽器端儲存,但特性大不相同:

特性IndexedDBlocalStorage
儲存容量50MB ~ 數 GB5 ~ 10 MB
資料型態物件、陣列、二進制僅字串
查詢功能索引、範圍查詢僅鍵值查詢
API 設計非同步同步
交易支援
效能大資料量下較優小資料量下較簡單
使用難度中高

選擇建議:

  • ✅ localStorage:儲存簡單設定、小量使用者偏好
  • ✅ IndexedDB:離線資料、大量快取、結構化資料

總結與選擇建議

Redis、SQLite 與 IndexedDB 各有其最佳應用場景,選擇時應考慮:

  1. 運行環境:伺服器、客戶端應用或瀏覽器
  2. 資料特性:結構化程度、資料量、存取模式
  3. 效能需求:並發量、延遲要求、讀寫比例
  4. 持久性需求:臨時快取或長期儲存
  5. 開發與維護成本:團隊技術棧、運維能力

核心原則:

  • Redis:追求極致效能的伺服器端快取與即時資料處理
  • SQLite:需要完整 SQL 功能的本地端、嵌入式場景
  • IndexedDB:瀏覽器端大容量資料儲存與離線功能

實務上,多數複雜應用會混合使用多種儲存技術,各司其職,發揮最大效益。正確的技術選擇能顯著提升系統效能、降低成本、改善用戶體驗。