cover

資料庫全景:從 Google Sheets 到 Vector DB

這篇文章不是一份資料庫產品比較表。它是一條路線,從每個人都用過的 Google Sheets 開始,一路走到 AI 時代的 Vector Database。每一站都在回答同一個問題:「上一個為什麼不夠用了?」 當你理解了這條路線上的每一個轉折點,你就能在面對任何專案時,做出合理的資料庫選型決策。

不是每個專案都需要走完全程。事實上,大多數專案停在前幾站就已經足夠了。知道「不需要什麼」和知道「需要什麼」一樣重要。

架構概覽

flowchart TD
    subgraph Relational["關聯式 Relational"]
        SQLite["SQLite\n嵌入式"]
        PG["PostgreSQL\n通用 RDBMS"]
        MySQL["MySQL\nWeb 主流"]
    end

    subgraph Document["文件型 Document"]
        Mongo["MongoDB\nJSON 文件"]
        Firestore["Firestore\n即時同步"]
    end

    subgraph KV["Key-Value / Cache"]
        Redis["Redis\n記憶體快取"]
        Memcached["Memcached\n純快取"]
    end

    subgraph Search["搜尋引擎"]
        ES["Elasticsearch\n全文搜尋"]
    end

    subgraph Vector["向量資料庫"]
        Pinecone["Pinecone"]
        pgvector["pgvector"]
    end

    Sheets["Google Sheets\n起點"] --> SQLite
    SQLite --> PG
    PG --> Mongo
    PG --> Redis
    PG --> ES
    ES --> Pinecone

為什麼從 Google Sheets 開始

打開一個 Google Sheets,建立一個表頭(Name、Email、Phone),然後開始填資料。恭喜,你已經在使用資料庫了。

這不是比喻。試算表就是資料庫的最原始形態:

  • Rows = Records(一筆一筆的資料)
  • Columns = Fields(每筆資料的欄位)
  • Sheets = Tables(不同主題的資料分開放)
  • Filter / Sort = Query(查詢)
  • VLOOKUP = JOIN(跨表查詢)

每個人都用過試算表。每個人都做過「用 Excel 管理客戶資料」這件事。這就是我們旅程的起點。從這裡開始,隨著資料量增加、使用者變多、查詢變複雜,每一個階段碰到的瓶頸,都催生了下一代解決方案。


Level 0:Google Sheets / Excel

Google Sheets 能做的事比很多人想像的多。把它當作「最低門檻的資料庫」來看,它的功能其實相當完整。

能做什麼

=FILTER(A2:D100, B2:B100="Active")          # 篩選活躍客戶
=SORT(A2:D100, 3, FALSE)                     # 按第 3 欄降序排列
=VLOOKUP("Alice", Sheet2!A:C, 3, FALSE)      # 跨表查詢 Alice 的資料
=UNIQUE(B2:B100)                              # 取得不重複的值
=COUNTIF(C2:C100, ">1000")                   # 統計金額超過 1000 的筆數
  • 多人協作:天生支援多人同時編輯。行銷追蹤 campaign、業務管理客戶名單、PM 做 sprint backlog。打開瀏覽器就能用。
  • 自動化:Google Apps Script 可以寫自動寄信、自動整理報表、串接外部 API。
  • 視覺化:內建圖表功能,選取資料就能產生長條圖、圓餅圖、折線圖。
  • 成本:免費。零設定。零維護。啟動成本為零。

為什麼不夠

但當你的資料開始長大,問題就來了。

1. 沒有資料型別約束

同一欄可以混著放任何型別的資料,Sheets 不會阻止你:

手機號碼欄位:
  0912345678    → Sheets 當數字,自動去掉前導 0 → 變成 912345678
  "0912345678"  → 加引號才能保留前導零
  09-1234-5678  → 變成日期格式 Sep 1234, 5678

資料品質完全依賴人工自律。10 個人一起編輯一張表,一定會有人在「金額」欄位裡打「待確認」。

2. 行數限制與效能退化

Google Sheets 最多 10,000,000 cells。20 個欄位就是 50 萬行。但還沒到上限,效能就開始崩了:

資料量          體驗
< 1,000 行     順暢
1,000-10,000   開始感覺延遲
10,000-50,000  明顯卡頓
> 100,000      基本不可用

3. 沒有真正的關聯

VLOOKUP 模擬「關聯」,但客戶表欄位順序一改,所有公式都要跟著改。多層關聯(訂單 → 客戶 → 地區 → 業務負責人)幾乎不可能。一對多關係處理不了。

4. 沒有交易安全

「轉帳」操作:A 的餘額減 1000,B 的餘額加 1000。如果改了 A 之後 Apps Script 出錯中斷,A 的錢就憑空消失了。沒有 rollback 機制。

5. 並行編輯衝突

兩個人同時改同一個 cell,最後寫入的人覆蓋前一個人的修改,沒有任何提示。批次更新和手動修改同時進行,結果不可預測。

When you outgrow this → SQLite

當你需要真正的資料型別、處理超過十萬筆資料、或者需要 transaction 安全性,你就需要一個真正的資料庫。而最低門檻的真正資料庫就是 SQLite。


Level 1:SQLite

SQLite 是世界上部署量最大的資料庫引擎。你的手機裡現在就有幾十個 SQLite 資料庫在運作:iOS 的通訊錄、Android 的簡訊、Chrome 的書籤、LINE 的聊天記錄。不需要安裝伺服器,整個資料庫就是一個檔案。

解決了什麼

真正的資料型別 + SQL 查詢

CREATE TABLE customers (
    id       INTEGER PRIMARY KEY AUTOINCREMENT,
    name     TEXT NOT NULL,
    email    TEXT UNIQUE NOT NULL,
    balance  REAL DEFAULT 0.0,
    created  TEXT DEFAULT (datetime('now'))
);
 
-- VLOOKUP 做不到的:多層 JOIN
SELECT o.id, c.name, r.name AS region, s.name AS sales_rep
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN regions r ON c.region_id = r.id
JOIN sales_reps s ON r.rep_id = s.id
WHERE o.created_at > '2024-01-01';
 
-- VLOOKUP 做不到的:聚合分析
SELECT strftime('%Y-%m', created_at) AS month,
       COUNT(*) AS order_count, SUM(amount) AS total
FROM orders GROUP BY month ORDER BY month;

單一檔案,零設定

sqlite3 myapp.db                     # 建立資料庫,沒有安裝步驟
cp myapp.db myapp_backup.db          # 備份 = 複製檔案
scp myapp.db user@server:/data/      # 搬遷 = 帶走檔案

ACID 交易

BEGIN TRANSACTION;
    UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
    UPDATE accounts SET balance = balance + 1000 WHERE id = 2;
COMMIT;
-- 中間出錯,兩個操作都不會生效(全部 rollback)

處理百萬級資料:有 index 的情況下,10 萬到 100 萬行仍然是毫秒到秒級回應。

使用情境

  • 手機 App:Core Data(iOS)和 Room(Android)底層就是 SQLite
  • 桌面應用:Chrome / Firefox 的書籤、歷史紀錄、Cookie
  • 小型 Web 應用:Django 的預設資料庫就是 SQLite
  • Prototyping:初期用 SQLite 快速迭代,上線再切 PostgreSQL
  • 資料分析:用 SQL 查 CSV 匯入的資料,比 pandas 直覺得多

為什麼不夠

1. 單一寫入者限制:寫入時鎖住整個檔案,同一時間只能一個 process 寫入。多個 Web worker 每次寫入都互相等待。

2. 沒有網路存取:檔案在哪台機器,就只有那台能存取。多台 server 無法共用。放在 NAS 上?file locking 在網路檔案系統上不可靠,會造成資料損壞。

3. 沒有使用者管理:任何能讀取檔案的 process 都能讀寫資料庫。無法設定角色權限。

4. 沒有 Replication:硬碟壞了資料就沒了。沒有 point-in-time recovery。

When you outgrow this → PostgreSQL / MySQL

當你需要多用戶同時讀寫、從不同機器存取、權限管理、replication 和自動備份,你就需要 client-server 架構的 RDBMS。


Level 2:PostgreSQL / MySQL(RDBMS)

從 SQLite 到 PostgreSQL,最核心的改變是架構:從「嵌入式檔案」變成「獨立的伺服器」。資料庫變成持續運行的 service,透過 TCP 接受連線,支援多使用者同時讀寫。

解決了什麼

Client-Server 架構:所有服務透過網路連到同一個 PostgreSQL,不再受限於檔案在哪台機器。

並行讀寫:MVCC(Multi-Version Concurrency Control)讓多個 transaction 同時讀寫不互相 block。

使用者管理和存取控制

CREATE USER app_readonly WITH PASSWORD 'readonly_pass';
CREATE USER app_readwrite WITH PASSWORD 'rw_pass';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_readwrite;
-- API 服務用 readwrite,報表服務用 readonly(防止誤修改)

Replication + Point-in-Time Recovery

Primary (寫入 + 讀取)
    ├── WAL Stream ──→ Replica 1 (唯讀,報表查詢)
    ├── WAL Stream ──→ Replica 2 (唯讀,分散讀取負載)
    └── WAL Archive ──→ Object Storage (PITR)

豐富的資料型別

-- JSONB:半結構化資料
CREATE TABLE products (
    id    SERIAL PRIMARY KEY,
    name  TEXT NOT NULL,
    attrs JSONB  -- 不同產品有不同屬性
);
SELECT name, attrs->>'cpu' FROM products WHERE attrs->>'ram' = '16';
 
-- 全文搜尋
SELECT title, ts_rank(to_tsvector('english', content), query) AS rank
FROM articles, to_tsquery('english', 'database & performance') query
WHERE to_tsvector('english', content) @@ query ORDER BY rank DESC;
 
-- 地理空間(PostGIS extension)
SELECT name, ST_Distance(location, ST_MakePoint(121.5654, 25.0330)) AS distance
FROM stores ORDER BY distance LIMIT 5;

PostgreSQL vs MySQL 比較

特性PostgreSQLMySQL
SQL 標準遵循非常高中等
JSON 支援優秀(JSONB + index)良好(MySQL 8.0+)
全文搜尋內建(tsvector)內建(FULLTEXT)
Extension 生態豐富(PostGIS, pgvector, pg_trgm)有限
Partial Index支援不支援
Array / Range 型別支援不支援
典型場景複雜查詢、資料完整性高讀取吞吐量

選擇建議:2024 年新專案建議選 PostgreSQL。JSONB + FTS 可以延後引入 MongoDB 和 Elasticsearch 的時間點。

為什麼不夠(某些場景)

1. Schema 僵硬性

-- products 表有 1 億行
ALTER TABLE products ADD COLUMN sku TEXT NOT NULL DEFAULT 'UNKNOWN';
-- 需要回填 1 億行,可能幾小時,期間可能鎖表影響線上服務
 
ALTER TABLE products ALTER COLUMN price TYPE NUMERIC(10,2);
-- 改欄位型別需要重寫整張表

2. 巢狀資料不擅長:一個商品目錄在 RDBMS 裡需要 5+ 張表和多個 JOIN,在 MongoDB 裡可能是一個 document。

3. 水平擴展困難:PostgreSQL 設計上是 Scale Up(加 CPU、記憶體),不是 Scale Out(加機器)。Sharding 非常痛苦——自己處理分片邏輯、跨片查詢、rebalancing。

4. JOIN 密集查詢在大規模時變慢:5 張表、多個 JOIN、聚合計算,每張表幾千萬行時,查詢可能需要幾十秒。

When you outgrow this → MongoDB(在某些使用場景下)

PostgreSQL 能處理的範圍比大多數人以為的大得多。只有在 schema 極度不固定、需要水平擴展、資料天生巢狀結構的條件下,MongoDB 才是更好的選擇。


Level 3:MongoDB(Document Store)

MongoDB 用「document」取代「row」,用「collection」取代「table」。每個 document 是一個 JSON(BSON)物件,可以有完全不同的結構。

解決了什麼

Schema 彈性

// 同一個 collection,不同結構,不需要 ALTER TABLE
db.products.insertOne({
  name: "MacBook Pro 14", category: "electronics", price: 1999,
  specs: { cpu: "M3 Pro", ram: 18, storage: "512GB SSD" },
  ports: ["HDMI", "MagSafe", "USB-C", "USB-C", "USB-C"]
});
 
db.products.insertOne({
  name: "Classic T-Shirt", category: "clothing", price: 29.99,
  sizes: ["S", "M", "L", "XL"], materials: ["100% Cotton"],
  variants: [
    { color: "White", sku: "TSH-W-001", stock: 150 },
    { color: "Black", sku: "TSH-B-001", stock: 230 }
  ]
});

巢狀 Document(免 JOIN)

// RDBMS 需要 4 張表 + 3 個 JOIN,MongoDB 一個 document 搞定
db.orders.insertOne({
  orderNumber: "ORD-2024-0001", status: "shipped",
  customer: { name: "Alice Chen", email: "alice@example.com",
              address: { street: "信義路五段 7 號", city: "台北市" } },
  items: [
    { product: "MacBook Pro 14", quantity: 1, price: 1999 },
    { product: "USB-C Hub", quantity: 2, price: 49.99 }
  ],
  payment: { method: "credit_card", amount: 2098.98 }
});
 
// 查詢完整訂單 — 一次讀取,零 JOIN
const order = await db.orders.findOne({ orderNumber: "ORD-2024-0001" });

水平擴展內建:Sharding 是 MongoDB 的原生功能。加 shard 不需停機,Router 自動路由查詢。

地理空間查詢:GeoJSON 原生支援,找附近 2 公里內的餐廳一行搞定。

使用情境

  • CMS:每篇文章的 metadata 可能不同(video、gallery、embed)
  • IoT 資料收集:不同裝置送來格式不同的資料
  • 使用者 Profile:不同使用者有不同的偏好設定和第三方帳號連結
  • 快速 Prototype:不確定資料結構會怎麼變化時

為什麼不夠

1. 跨 Collection Transaction:MongoDB 4.0+ 支援,但效能開銷比 RDBMS 大很多。需要大量 transaction 的場景(銀行、帳務),PostgreSQL 仍更好。

2. 資料重複:為避免 JOIN 把客戶資料嵌入訂單。客戶搬家了,1000 筆歷史訂單裡的地址要不要更新?RDBMS 裡 UPDATE 一行就好,所有 JOIN 自動拿到新地址。

3. “Schema-less” = “Schema-chaos”

// 開發者 A:{ userName: "alice", emailAddress: "..." }
// 開發者 B:{ user_name: "bob", email: "..." }
// 開發者 C:{ name: "charlie", mail: "..." }
// 三個月後,users collection 裡有三種 schema

4. 儲存開銷:每個 document 都存一遍 field name。百萬個 document 的 "customerName""shippingAddress" 比 RDBMS column header 佔更多空間。

誠實的觀點

大部分選擇 MongoDB 的新創團隊其實不需要 MongoDB。PostgreSQL JSONB 可以處理 90% 的「我們需要彈性 schema」場景:

CREATE TABLE products (
    id         SERIAL PRIMARY KEY,
    name       TEXT NOT NULL,
    price      NUMERIC(10,2) NOT NULL,
    attributes JSONB  -- 彈性的部分放這裡
);
CREATE INDEX idx_attrs ON products USING GIN(attributes);
SELECT * FROM products WHERE attributes @> '{"color": "black"}';

固定欄位有 schema 保護,彈性欄位用 JSONB。兩全其美。

只有在以下條件全部成立時才真正需要 MongoDB:

  1. 資料量大到需要水平擴展(百億級)
  2. 資料結構真的非常不固定
  3. 不需要複雜的跨 collection transaction
  4. 團隊有 MongoDB 運維經驗

Level 4:Redis(In-Memory Store)

到目前為止,所有資料庫都把資料存在磁碟上(毫秒級)。Redis 把所有資料放在記憶體裡,讀寫速度快 100 倍以上。

Redis 不是用來取代 PostgreSQL 的。它是「加速器」,放在主資料庫前面,處理需要極快回應的場景。

解決了什麼

微秒級回應

操作                PostgreSQL    Redis
簡單 SELECT         ~1-5ms        ~0.1ms
計數器 +1           ~5ms          ~0.05ms

Session 儲存:每個 HTTP request 都要查 session。每秒 1000 個 request,用 PostgreSQL 是 3 秒的 DB 時間,用 Redis 是 0.1 秒。

快取(Cache)

def get_product(product_id: str):
    cached = r.get(f"product:{product_id}")
    if cached:
        return json.loads(cached)  # cache hit,0.1ms
    product = db.query("SELECT * FROM products WHERE id = %s", product_id)
    r.setex(f"product:{product_id}", 300, json.dumps(product))  # 5 分鐘過期
    return product
# 90% 的查詢命中 cache → PostgreSQL 壓力降低 90%

速率限制:用 INCR + EXPIRE 實作每個 IP 每分鐘最多 N 次請求。

排行榜:Sorted Set 天生就是做排行榜的。ZADDZREVRANGE,O(log(N)) 超快。

Pub/Sub:即時通知推送。

分散式鎖:防止多個 worker 同時處理同一筆訂單。

為什麼不夠

1. 記憶體限制:存 1TB 資料,PostgreSQL(SSD)約 3,000-5,000/月。Redis 只存「熱資料」。

2. 持久化是 best-effort:即使開了 AOF,極端情況仍可能遺失最近幾秒寫入。不適合作為資料唯一來源。

3. 不適合複雜查詢:沒有 SQL,只能按 key 查詢。

4. 不是主資料庫:Redis 掛了,應用 fallback 到 PostgreSQL 繼續運作(只是變慢)。PostgreSQL 掛了才是真正的災難。

Redis 不會取代 PostgreSQL,它是 PostgreSQL 的最佳搭檔。 大多數 production 系統的標準配置是 PostgreSQL + Redis。


100 萬件商品,使用者搜「藍色 防水 跑步鞋」。PostgreSQL 怎麼查?

-- LIKE:full table scan,超慢
SELECT * FROM products WHERE name LIKE '%藍色%' AND name LIKE '%防水%';
 
-- PostgreSQL FTS:好很多,但 relevance scoring 調整空間有限
SELECT * FROM products
WHERE to_tsvector('chinese', name || ' ' || description)
   @@ to_tsquery('chinese', '藍色 & 防水 & 跑步鞋');

PostgreSQL FTS 對「中小規模搜尋」夠用。但當搜尋是核心功能——商品搜尋、日誌分析、知識庫——你需要 Elasticsearch。

解決了什麼

百萬級全文搜尋 + 相關性評分:支援同義詞(跑步鞋 = 慢跑鞋 = running shoes)、權重設定(名稱匹配權重 3 倍於描述)、highlight 標記匹配片段。

Faceted Search:一次查詢同時回傳搜尋結果 + 品牌分布 + 價格區間分布 + 顏色分布 + 平均評分。電商左側篩選條件的標準實作。

日誌分析(ELK Stack)

Application → Logstash/Filebeat → Elasticsearch → Kibana
  logs          收集 & 轉換          儲存 & 搜尋     視覺化

# 「過去 1 小時,所有包含 'timeout' 的 error log」— 秒級回應

PostgreSQL FTS vs Elasticsearch 比較

特性PostgreSQL FTSElasticsearch
安裝複雜度零(內建)需要額外部署 cluster
查詢效能(< 1000 萬筆)優秀優秀
查詢效能(1 億+筆)退化明顯維持良好
相關性調整基本(ts_rank)極度靈活
多語言支援需個別設定內建 analyzer
Faceted Search需多個 query內建 aggregation
運維複雜度高(cluster 管理)
適用規模小中型搜尋搜尋即核心功能

判斷標準

「讓使用者能在 App 裡搜尋文章」→ PostgreSQL FTS 足矣
「搜尋是產品核心功能」→ Elasticsearch
「幾百萬筆 log 即時搜尋」→ ELK Stack

簡單的判斷:PostgreSQL FTS 處理「在 app 裡加搜尋功能」;Elasticsearch 處理「搜尋本身就是 app」。

更多 PostgreSQL FTS 細節參考 PostgreSQL 進階,Elasticsearch 深入操作參考 Elasticsearch 深入


Level 6:Vector Database(AI 時代)

到目前為止,所有搜尋都是「關鍵字匹配」。搜尋「怎麼讓團隊更有效率」時,你也想找到講「生產力」、「敏捷開發」、「時間管理」的文章——這些文章可能完全沒有「效率」這兩個字,但語意上相關。

這就是 Vector Database 要解決的問題。

核心概念:Embedding

AI 模型可以把文字轉成一組數字(向量,通常 1536 維)。語意相近的文字,向量在數學上很接近。

from openai import OpenAI
client = OpenAI()
 
v1 = get_embedding("怎麼讓團隊更有效率")
v2 = get_embedding("提升團隊生產力的方法")    # 語意相近 → 距離小
v3 = get_embedding("今天台北天氣如何")          # 語意無關 → 距離大
 
# cosine_similarity(v1, v2) ≈ 0.92
# cosine_similarity(v1, v3) ≈ 0.15

解決了什麼

語意搜尋:搜「怎麼減少部署風險」,PostgreSQL FTS 找不到標題是「Blue-Green Deployment 實踐」的文章(沒有「風險」這個字)。Vector Search 會找到,因為語意相關。

RAG(Retrieval-Augmented Generation)

# 讓 LLM 根據你的資料回答問題
def answer_question(user_query: str) -> str:
    query_vector = get_embedding(user_query)
 
    # 在 Vector DB 找最相關的 5 個文件
    results = vector_db.query(vector=query_vector, top_k=5)
 
    # 把文件作為 context 送給 LLM
    context = "\n\n".join([r.metadata['content'] for r in results])
    response = client.chat.completions.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": f"根據以下文件回答問題。\n\n{context}"},
            {"role": "user", "content": user_query}
        ]
    )
    return response.choices[0].message.content
 
# 使用者問「我們的年假政策是什麼?」
# Vector DB 找到公司 HR 文件 → LLM 根據文件回答

其他應用:以圖搜圖、推薦系統、異常偵測。

Vector Database 選型

方案特色適用場景
pgvector零額外部署,SQL 操作< 500 萬向量
Pinecone全托管 SaaS快速上線、不想管基礎設施
Weaviate開源,hybrid searchkeyword + vector 搜尋
Milvus開源,大規模億級向量
QdrantRust 寫的,效能好中大規模

如果已經在用 PostgreSQL,先試 pgvector。幾十萬到幾百萬個向量足夠了,不需要多維護一個資料庫。

CREATE EXTENSION vector;
CREATE TABLE documents (
    id        SERIAL PRIMARY KEY,
    title     TEXT NOT NULL,
    content   TEXT NOT NULL,
    embedding vector(1536)
);
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
 
-- 語意搜尋:找最相似的 5 個文件
SELECT title, 1 - (embedding <=> '[0.1, 0.2, ...]'::vector) AS similarity
FROM documents ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector LIMIT 5;

更多 RAG 架構細節參考 RAG 架構


選型決策樹

你的資料量?
│
├── < 1,000 筆,團隊 < 5 人
│   └── Google Sheets(零成本、零設定)
│
├── < 100 萬筆,單機應用
│   └── SQLite(零設定、嵌入式、ACID)
│
├── 需要多用戶/多服務存取
│   └── PostgreSQL(JSONB + FTS + PostGIS)
│
├── Schema 極度不固定 + 水平擴展
│   └── MongoDB(⚠️ 先確認 PostgreSQL JSONB 不夠用)
│
├── 需要毫秒級快取 / Session
│   └── Redis(搭配 PostgreSQL)
│
├── 搜尋是核心功能
│   └── 小規模 → PostgreSQL FTS
│       大規模 → Elasticsearch
│
└── 需要語意搜尋(AI / LLM)
    └── pgvector(小規模)或專用 Vector DB(大規模)

原則:先用最簡單的方案,碰到瓶頸再升級。


組合使用的常見架構

每多一個資料庫,就多一份運維負擔。以下是從簡單到複雜的四種典型架構。

1. Small SaaS:PostgreSQL Only

Application ──→ PostgreSQL (JSONB + FTS)

運維複雜度:★☆☆☆☆
適用:MVP、使用者 < 10,000

大部分新專案從這裡開始。Instagram 被 Facebook 收購時(3000 萬用戶),核心也只是 PostgreSQL。

2. Medium SaaS:PostgreSQL + Redis

API Server ──→ Redis (cache/session)
           ──→ PostgreSQL (主資料)
Worker     ──→ Redis (job queue)
           ──→ PostgreSQL

運維複雜度:★★☆☆☆
適用:使用者 10,000 - 100,000

90% 的 Web 應用用這個組合就夠了。

3. Content Platform:PostgreSQL + Elasticsearch + Redis

API Server ──→ Redis (cache)
           ──→ PostgreSQL (主資料,寫入)
           ──→ Elasticsearch (搜尋,讀取)
Sync Worker → PostgreSQL → Elasticsearch (資料同步)
Log Pipeline → Elasticsearch (log index)

運維複雜度:★★★☆☆
適用:電商、媒體、知識庫

PostgreSQL 仍是 source of truth。所有寫入先到 PostgreSQL,再同步到 Elasticsearch。同步方式:CDC(Debezium)、定時批次、或 application layer dual write。

4. AI Application:PostgreSQL + Vector DB + Redis

API Server  ──→ Redis (cache)
            ──→ PostgreSQL (主資料)
            ──→ pgvector / Pinecone (向量搜尋)
RAG Service ──→ Vector DB (context retrieval)
            ──→ LLM (answer generation)

運維複雜度:★★★☆☆ (pgvector) / ★★★★☆ (獨立 Vector DB)
適用:AI 產品、企業知識庫 chatbot

常見問題與風險

”MongoDB because it’s web scale”

CTO:「我們用 MongoDB,因為未來要水平擴展。」
工程師:「現在多少用戶?」
CTO:「500。」

# 500 個用戶。SQLite 都綽綽有餘。

Premature optimization 是軟體工程最大的陷阱。1000 萬筆資料前不需要想 sharding。10 萬筆前不需要想 Elasticsearch。

把 Redis 當主資料庫

Redis 重啟可能遺失資料、記憶體不夠會 evict 資料、沒有 SQL 做報表。主資料存 PostgreSQL,熱資料快取在 Redis。

沒有善用 PostgreSQL 的內建功能

-- 你以為需要 MongoDB 的事:
SELECT * FROM products WHERE attributes @> '{"color": "blue", "size": "L"}';
 
-- 你以為需要 Elasticsearch 的事:
SELECT title, ts_rank(tsv, query) AS rank
FROM articles, to_tsquery('english', 'database & optimization') query
WHERE tsv @@ query ORDER BY rank DESC LIMIT 20;

原則:先把 PostgreSQL 的功能用到極致,再考慮引入其他資料庫。

每多一個資料庫 = 多一份運維負擔

PostgreSQL 需要備份、監控、升級、調校。加 Redis 多了記憶體監控和淘汰策略。加 Elasticsearch 多了 cluster 管理和資料同步 pipeline。加 Vector DB 多了 embedding 更新和成本管理。一個人的團隊管 4 個資料庫 = 災難。

資料一致性

多資料庫時,更新一筆資料要同步到 PostgreSQL + Elasticsearch + Redis。其中一個失敗了,使用者看到的資料取決於有沒有命中 cache。解決方式:合理的 cache TTL、最終一致性、CDC 自動同步。


一張圖看完全部

            資料量
            ▲
  100 億+   │                            ┌─────────┐
            │                            │ MongoDB │
   1 億+    │          ┌─────────────┐   │(sharding)│
            │          │Elasticsearch│   └─────────┘
  1000 萬+  │          └─────────────┘
            │   ┌────────────┐
   100 萬+  │   │ PostgreSQL │ ← 大部分專案停在這裡就夠了
            │   └────────────┘
    10 萬+  │   ┌────────┐
            │   │ SQLite │
     1 萬+  │   └────────┘
            │   ┌───────────────┐
     1000   │   │ Google Sheets │
            │   └───────────────┘
            └───────────────────────────→ 功能複雜度

搭配使用:
Redis ──────── 快取/Session,幾乎永遠搭配主資料庫
Vector DB ──── AI/語意搜尋,搭配主資料庫

延伸閱讀