結論先講

2026 年,除非你有非常明確的理由,否則預設選 PostgreSQL。 MongoDB 適合的場景比大多數人想的要窄——CMS 內容管理、IoT 事件日誌、快速原型。如果你的資料有任何「關聯」需求(使用者有訂單、訂單有商品),關聯式資料庫永遠是更安全的選擇。

一個殘酷的統計:在我見過的 MongoDB 專案中,超過一半最終都遷回了 PostgreSQL 或 MySQL。而反過來的案例,幾乎沒有。


MongoDB 為什麼那麼紅?

2010 年代初期,MongoDB 的行銷策略非常成功。它打中了開發者幾個痛點:

  1. 不用寫 schema——丟 JSON 進去就好,改格式不用跑 migration
  2. JavaScript 全棧——Node.js + Express + MongoDB(MEAN/MERN stack),前後端都是 JSON
  3. 水平擴展容易——內建 sharding,「web scale」的口號很吸引人
  4. 入門超簡單——db.users.insertOne({name: "Terry"}) 就存進去了
// MongoDB 的魅力:存什麼都行
db.products.insertOne({
  name: "筆電",
  price: 35000,
  specs: {
    cpu: "M4 Pro",
    ram: "36GB",
    storage: "1TB SSD"
  },
  tags: ["apple", "laptop", "2025"],
  reviews: [
    { user: "Alice", rating: 5, comment: "超讚" },
    { user: "Bob", rating: 4, comment: "不錯" }
  ]
});

看起來很美好,對吧?問題在後面。


MongoDB 的坑

1. 「Schema-less 是個謊言」

MongoDB 號稱 schema-less,但你的應用程式一定有 schema。差別只是這個 schema 從資料庫層搬到了應用層。

// 同一個 collection 裡可能有這些混亂的資料
{ name: "Alice", age: 25 }
{ name: "Bob", age: "thirty" }      // age 變字串了
{ Name: "Charlie", AGE: 25 }        // 欄位名大小寫不同
{ name: "Dave" }                     // 少了 age
{ name: "Eve", age: 25, email: null } // 多了 email

沒有 schema 不代表不需要 schema,只代表沒有人幫你檢查。 三個月後你的 collection 就是一團亂,而且 MongoDB 不會告訴你。

2. 沒有真正的 JOIN

MongoDB 有 $lookup(3.2+ 版本加的),但它跟 SQL 的 JOIN 比起來:

// MongoDB $lookup(模擬 JOIN)
db.orders.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "userId",
      foreignField: "_id",
      as: "user"
    }
  },
  { $unwind: "$user" },
  {
    $lookup: {
      from: "products",
      localField: "items.productId",
      foreignField: "_id",
      as: "products"
    }
  }
]);
-- PostgreSQL JOIN(清楚多了)
SELECT o.*, u.name, p.title
FROM orders o
JOIN users u ON o.user_id = u.id
JOIN order_items oi ON oi.order_id = o.id
JOIN products p ON p.id = oi.product_id;

$lookup 在跨 shard 的情況下效能更差,而且不支援索引優化。

3. 交易支援來得太晚

MongoDB 4.0(2018 年)才支援多文件交易,4.2 才支援分散式交易。這代表在此之前,「扣庫存 + 建立訂單」這種基本操作都沒辦法保證原子性。

// MongoDB 交易(4.0+)
const session = client.startSession();
try {
  session.startTransaction();
  await db.inventory.updateOne(
    { sku: "abc123", qty: { $gte: 1 } },
    { $inc: { qty: -1 } },
    { session }
  );
  await db.orders.insertOne(
    { sku: "abc123", userId: "user1" },
    { session }
  );
  await session.commitTransaction();
} catch (e) {
  await session.abortTransaction();
} finally {
  session.endSession();
}

能用,但效能比單文件操作慢很多,而且 MongoDB 官方自己都說「如果你需要交易,考慮把相關資料放在同一個文件」。


PostgreSQL JSONB:兩個世界的交集

PostgreSQL 的 JSONB 欄位類型讓你在關聯式資料庫裡享受 Document DB 的彈性:

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    price DECIMAL(10,2) NOT NULL,
    -- 結構化欄位放外面,彈性部分用 JSONB
    specs JSONB,
    metadata JSONB
);
 
-- 插入
INSERT INTO products (name, price, specs, metadata)
VALUES (
    '筆電',
    35000,
    '{"cpu": "M4 Pro", "ram": "36GB", "storage": "1TB SSD"}',
    '{"tags": ["apple", "laptop"], "reviews_count": 42}'
);
 
-- 查詢 JSON 欄位
SELECT name, specs->>'cpu' AS cpu, price
FROM products
WHERE specs->>'ram' = '36GB'
AND price < 40000;
 
-- JSON 欄位建索引(GIN 索引)
CREATE INDEX idx_products_specs ON products USING GIN (specs);
 
-- 用 @> 運算子查包含關係(走 GIN 索引)
SELECT * FROM products
WHERE specs @> '{"cpu": "M4 Pro"}';

關鍵差異:你可以把「確定的結構」用正常欄位存(有 constraint、有型別檢查),「不確定的部分」用 JSONB 存。兩全其美。


正面對決比較表

面向MongoDBPostgreSQL
ACID 交易4.0+ 支援,效能較差原生支援,成熟穩定
Schema動態(自由但危險)嚴格 + JSONB 彈性
JOIN$lookup(有限制)完整 JOIN 支援
查詢語言MQL(JSON-based)SQL(通用標準)
水平擴展內建 Sharding需要 Citus 或手動
垂直效能
全文搜尋Atlas Searchtsvector / pg_trgm
地理查詢內建 GeoJSONPostGIS(業界標準)
彈性結構原生優勢JSONB(夠用但稍麻煩)
工具生態Compass, AtlaspgAdmin, DBeaver, 任何 SQL 工具
ORM 支援Mongoose所有主流 ORM
託管服務AtlasRDS, Cloud SQL, Supabase, Neon
學習資源極多

MongoDB 真正適合的場景

公平地說,有些場景 MongoDB 確實比 PostgreSQL 更合適:

1. 內容管理系統(CMS)

文章、頁面、widget——每種內容的結構都不同,而且經常變動。用 MongoDB 存,改結構不用跑 migration。

// CMS 場景:每篇文章結構可能不同
db.pages.insertOne({
  type: "blog",
  title: "Hello World",
  body: "...",
  blocks: [
    { type: "text", content: "..." },
    { type: "image", url: "...", alt: "..." },
    { type: "code", language: "python", content: "..." }
  ]
});

2. IoT 事件日誌

大量寫入、結構不固定、很少需要 JOIN。

3. 快速原型 / MVP

不確定 schema 長什麼樣子,先快速迭代。但請在確定 schema 後遷到 PostgreSQL

4. 遊戲玩家資料

每個玩家的裝備、成就、進度結構差異大,嵌套很深。


MongoDB 不適合的場景

場景為什麼不適合
電商 / 金融交易一致性是生命線,MongoDB 的 ACID 支援太晚太弱
複雜關聯使用者 → 訂單 → 商品 → 評論,多層 JOIN 是日常需求
報表分析GROUP BY、Window Functions、CTE——SQL 的天下
多表聚合查詢Aggregation Pipeline 寫起來比 SQL 痛苦十倍
需要嚴格資料完整性外鍵約束、CHECK 約束、UNIQUE 約束——MongoDB 都沒有

Firestore 與 DynamoDB:託管替代方案

如果你考慮 Document DB 但不想管 infra:

特性FirestoreDynamoDBMongoDB Atlas
雲端GCPAWS多雲
定價模型按讀寫次數按讀寫容量按節點規格
即時同步原生支援DynamoDB StreamsChange Streams
離線支援原生
適合行動 App、小型 Web大規模、低延遲任何 MongoDB 場景
學習曲線中高(分區鍵設計)
避免複雜查詢(索引限制)複雜查詢需要 JOIN 的場景

Firestore 適合前端直連(有內建安全規則),做行動 App 或即時協作很方便。 DynamoDB 適合超大規模、低延遲的 key-value 查詢,但分區鍵設計是門學問。


遷移故事:為什麼都是 MongoDB 搬回 PostgreSQL?

這不是偏見,是統計事實。常見的遷移劇本:

  1. 原型期:用 MongoDB 快速開發,schema 隨便改
  2. 成長期:開始需要 JOIN、交易、複雜查詢,$lookup 越寫越痛苦
  3. 成熟期:需要報表、需要分析、需要資料完整性,MongoDB 全部做不好
  4. 遷移:痛苦地把資料搬回 PostgreSQL,重寫 query layer

反過來的案例(PostgreSQL → MongoDB)極少見,因為:

  • PostgreSQL 有 JSONB,彈性需求已被滿足
  • SQL 的表達能力遠超 MQL
  • PostgreSQL 的生態(備份、監控、工具)更成熟

經典引言:「MongoDB is web scale」已經從讚美變成了 meme。


常見問題

PostgreSQL 的 JSONB 效能好嗎?

比 MongoDB 的原生 document 查詢稍慢(因為 JSONB 是二進位格式但存在 row 裡),但搭配 GIN 索引後差距不大。大部分應用場景感覺不出差異。關鍵是 JSONB 讓你在需要結構化的地方用欄位,需要彈性的地方用 JSON。

MongoDB Atlas 免費方案夠用嗎?

Atlas M0(免費)給 512MB 儲存,做個人專案或原型夠用。但限制很多:沒有備份排程、沒有 performance advisor、共享叢集效能不穩定。真的要上生產,M10 起跳(大約每月 60 美金)。

如果團隊只會 MongoDB 怎麼辦?

學 SQL。認真的。SQL 是資料領域的共通語言,MongoDB 的 MQL 只能在 MongoDB 裡用。花兩週學 SQL 的投資報酬率遠高於繼續在 MQL 的 aggregation pipeline 裡掙扎。

Mongoose 的 schema 不就解決了「schema-less 的問題」嗎?

部分解決。Mongoose 在應用層做 schema 驗證,但它不能防止有人直接用 MongoDB shell 寫入不合格的資料。而且 Mongoose 的 schema 不是資料庫層級的約束,migration 管理也比不上 Prisma / TypeORM 對 PostgreSQL 的支援。

MongoDB 7.0 / 8.0 有改善嗎?

有。MongoDB 持續在補強弱點——queryable encryption、column store index、更好的 aggregation pipeline 效能。但核心設計(document model)的限制還在:沒有外鍵約束、JOIN 效能不佳、schema 驗證是可選的。這些不是版本更新能根本解決的。


本系列文章

  1. 資料庫全景:SQL vs NoSQL vs NewSQL
  2. MySQL:為什麼越來越多人覺得它不夠好?
  3. PostgreSQL:為什麼越來越多人轉
  4. NoSQL 什麼時候該用
  5. 資料庫設計:正規化與索引策略
  6. 向量資料庫:RAG 時代的 Embedding 儲存方案
  7. 時序資料庫:監控資料用一般 DB 存,你會後悔
  8. MongoDB vs PostgreSQL:什麼時候該用 NoSQL?(本篇)
  9. 資料庫設計模式:正規化、反正規化、分表的取捨
  10. 擴展策略:讀寫分離與分片
  11. Docker 環境的資料庫管理
  12. 資料庫安全:從連線加密到欄位加密