
資料庫全景:從 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 比較
| 特性 | PostgreSQL | MySQL |
|---|---|---|
| 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 裡有三種 schema4. 儲存開銷:每個 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:
- 資料量大到需要水平擴展(百億級)
- 資料結構真的非常不固定
- 不需要複雜的跨 collection transaction
- 團隊有 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 天生就是做排行榜的。ZADD、ZREVRANGE,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。
Level 5:Elasticsearch(Full-Text Search)
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 FTS | Elasticsearch |
|---|---|---|
| 安裝複雜度 | 零(內建) | 需要額外部署 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 search | keyword + vector 搜尋 |
| Milvus | 開源,大規模 | 億級向量 |
| Qdrant | Rust 寫的,效能好 | 中大規模 |
如果已經在用 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/語意搜尋,搭配主資料庫
延伸閱讀
- Database(PostgreSQL) — PostgreSQL 單節點部署基線、PgBouncer、備份策略
- PostgreSQL 進階 — Full-text Search、GIN/GiST Index、進階查詢優化
- Elasticsearch 深入 — Cluster 管理、Index 策略、效能調校