資料儲存技術比較: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 支援:查詢功能較關聯式資料庫簡單

詳細比較表格

比較項目 Redis SQLite IndexedDB
資料模型 鍵值對 + 多種資料結構 關聯式(表格、欄位、索引) 鍵值對 + 物件儲存 + 索引
查詢語言 Redis 指令集 標準 SQL JavaScript API(無 SQL)
交易支援 有(MULTI/EXEC) 完整 ACID 交易 有(Transaction API)
持久化 可選(RDB、AOF) 預設持久化至檔案 預設持久化至瀏覽器儲存
並行處理 單執行緒 + 多工處理 多讀單寫 依瀏覽器實作
典型容量 數 GB(記憶體限制) 數 GB ~ 數十 GB 50MB ~ 數 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 大容量、瀏覽器原生支援
即時排行榜 Redis Sorted 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: 兩者都是瀏覽器端儲存,但特性大不相同:

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

選擇建議:

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

總結與選擇建議

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

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

核心原則:

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

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

Related Articles

Leave a Comment