# 從 Firestore 的蜜月期到 SQL 的深度思考:一個開發者的資料庫選擇之路

你知道嗎?有時候最痛苦的不是不知道該選什麼,而是選了之後才發現可能選錯了。
去年這個時候,我坐在咖啡店裡,筆電螢幕上開著兩個分頁。一個是 Firestore 的文件,另一個是 PostgreSQL 的教學。說實話,那時候的我完全不知道這個決定會如何改變接下來一整年的開發經歷。
## 初遇 Firestore:一見鍾情的開始
還記得第一次看到 Firestore 時的興奮感。那時候我們團隊正在開發一個即時聊天應用,時間壓力很大,投資人三週後就要看 demo。
「哇,這東西也太簡單了吧!」我記得當時對著同事大叫。
```javascript
// 就這樣,三行代碼搞定即時資料同步
db.collection('messages').onSnapshot((snapshot) => {
snapshot.docs.forEach((doc) => {
console.log(doc.data());
});
});
```
那時候的感覺就像是找到了程式界的聖杯。不用設定伺服器、不用擔心擴展性、不用處理複雜的 WebSocket 連線。更棒的是,資料會自動同步到所有裝置,連離線功能都內建好了。
我們花了兩天就把基本的聊天功能做出來了。兩天欸!要是用傳統的方式,光是設定資料庫和後端 API 就不知道要花多少時間。
投資人看了 demo 之後很滿意,資金到位,我們開始正式開發。那段時間真的是蜜月期,每個功能都做得飛快,程式碼乾淨簡潔,部署一鍵搞定。

## 現實開始咬人:成長的煩惱
好景不長。大概過了三個月,用戶數開始成長,問題也跟著來了。
第一個打擊是帳單。
「欸,這個月的 Firebase 帳單怎麼這麼高?」財務同事拿著帳單來找我,上面寫著 $847。
我心想不對啊,上個月才 $127,怎麼會暴增這麼多?仔細看了使用量報告才發現,隨著用戶增加,資料讀取次數呈指數型增長。每次用戶進入聊天室,系統就會載入所有歷史訊息,每則訊息都算一次讀取。
1000 個活躍用戶,每人每天平均 200 次資料操作,一個月就是 600 萬次操作。按照 Firestore 的計價方式(每 10 萬次讀取 $0.06),光是讀取操作一個月就要 $360。

更要命的是,我們根本無法準確預測下個月會花多少錢。
第二個問題是查詢限制。產品經理要求加一個「統計每個用戶本週發送訊息數量」的功能,聽起來很簡單對吧?
結果我發現 Firestore 無法做跨文件的聚合查詢。我不能寫類似這樣的查詢:
```sql
-- 這種查詢在 Firestore 裡根本不存在
SELECT user_id, COUNT(*) as message_count
FROM messages
WHERE created_at > '2024-01-01'
GROUP BY user_id
```
最後只能用笨方法,把每個用戶的所有訊息都載下來,在客戶端計算。用戶一多,這個方法就完全不可行了。
第三個痛點是資料關聯。我們想做一個「訊息被回覆」的功能,在 SQL 裡很簡單的 JOIN 操作,在 Firestore 變成了多層巢狀的複雜結構。
```javascript
// Firestore 的做法,要嵌套多層資料
messages/{messageId}/
content: "原始訊息"
author: {...}
replies: [
{ id: "reply1", content: "回覆1", author: {...} },
{ id: "reply2", content: "回覆2", author: {...} }
]
```
這樣做有個問題:如果回覆很多,整個文件會變得很大,而且每次載入原始訊息就要把所有回覆一起載入,非常浪費。
## SQL 的重新相遇:理性的回歸
就在我被這些問題搞得焦頭爛額的時候,公司來了一個資深後端工程師 David。
「你們為什麼不用 PostgreSQL?」他看了我們的架構後這樣問。
「因為 Firestore 比較簡單啊,而且有即時同步功能。」我有點不服氣地回答。
「簡單是沒錯,但是你看看這些查詢。」他指著我們的程式碼,「這些在 SQL 裡一行就能解決的事情,你們用了五六十行代碼,而且還要多次查詢。」
David 花了一個下午,把我們的資料模型用關聯式資料庫重新設計了一遍:
```sql
-- 用戶表
CREATE TABLE users (
id UUID PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(255) UNIQUE,
created_at TIMESTAMP
);
-- 聊天室表
CREATE TABLE chatrooms (
id UUID PRIMARY KEY,
name VARCHAR(100),
created_by UUID REFERENCES users(id),
created_at TIMESTAMP
);
-- 訊息表
CREATE TABLE messages (
id UUID PRIMARY KEY,
chatroom_id UUID REFERENCES chatrooms(id),
user_id UUID REFERENCES users(id),
content TEXT,
reply_to UUID REFERENCES messages(id),
created_at TIMESTAMP
);
```
然後他展示了一些查詢:
```sql
-- 統計每個用戶本週的訊息數量(一行搞定)
SELECT u.name, COUNT(m.id) as message_count
FROM users u
LEFT JOIN messages m ON u.id = m.user_id
WHERE m.created_at >= date_trunc('week', CURRENT_DATE)
GROUP BY u.id, u.name
ORDER BY message_count DESC;
-- 找出某則訊息的所有回覆(也是一行)
SELECT m.*, u.name as author_name
FROM messages m
JOIN users u ON m.user_id = u.id
WHERE m.reply_to = '某個訊息ID'
ORDER BY m.created_at;
```
看到這些查詢,我突然明白了什麼叫做「資料庫的力量」。
## 深度思考:兩種思維的碰撞
那晚回家後,我開始認真思考這兩種方案的差異。
Firestore 就像是一本靈活的筆記本,你想在哪一頁寫什麼都可以。有些頁面放清單、有些放圖片、有些放便條紙,完全自由。對於快速記錄想法來說很棒,但是當你想要分析筆記本裡的所有待辦事項,或者統計每個月寫了多少頁的時候,就會發現很困難。
SQL 資料庫更像是一套精心設計的檔案系統。每個資料夾都有清楚的標籤,每個文件都有固定的格式。雖然一開始需要花時間設計這套系統,但是之後要找任何資料或做任何分析都會非常快速準確。
這讓我想到了一個更深層的問題:**我們到底是要快速開發,還是要長期穩定?**
Firestore 的優勢很明顯:
- 開發速度快到飛起
- 自動處理擴展性問題
- 內建即時同步功能
- 不用管伺服器維護
但是它的限制也很明確:
- 複雜查詢能力有限
- 成本隨規模快速增長
- 資料關聯處理複雜
- 被綁定在 Google 生態系統
SQL 資料庫的優勢是:
- 查詢能力超級強大
- 成本相對可預測
- 資料完整性保證
- 技術生態成熟
但也有它的挑戰:
- 需要更多的架構設計
- 即時功能需要額外開發
- 伺服器管理和維護
- 擴展需要更多規劃
## 決策的時刻:什麼時候選什麼
經過幾個月的掙扎和測試,我總結出了一些選擇的判斷標準。
**如果你的專案符合以下條件,Firestore 會是很好的選擇:**
1. **快速原型或 MVP**:你需要在很短時間內展示一個可運作的產品
2. **即時協作功能**:聊天室、協作編輯、即時通知是核心需求
3. **移動應用為主**:主要用戶都是透過手機使用,且需要離線功能
4. **小團隊快速開發**:團隊沒有專門的後端或資料庫專家
5. **用戶規模可控**:月活用戶數在 10 萬以下,資料操作量不會太大
我們有個朋友的團隊做了一款即時協作筆記應用,用戶大概 2 萬人,每月 Firestore 費用控制在 $200 以內,開發和維護都很輕鬆。對他們來說,Firestore 就是完美選擇。
**但如果你的情況是這樣,強烈建議考慮 SQL:**
1. **複雜業務邏輯**:需要大量的資料關聯查詢和統計分析
2. **成本敏感**:希望能準確預測和控制資料庫成本
3. **大規模用戶**:月活用戶超過 10 萬,或資料操作量很大
4. **企業應用**:需要強資料一致性,涉及金流或重要商業邏輯
5. **長期專案**:這個產品會維護很多年,需要技術債務可控
我們最後的決定是什麼呢?
## 我們的選擇:混合策略
經過團隊討論,我們決定採用混合策略。
**即時功能繼續用 Firestore**:聊天室的即時訊息同步、線上用戶狀態、推播通知等,這些功能 Firestore 處理得很好,而且開發成本低。
**核心業務邏輯遷移到 PostgreSQL**:用戶管理、訊息歷史查詢、統計分析、報表生成等,這些都移到 PostgreSQL。
**資料同步機制**:我們設計了一個簡單的同步機制,重要資料會同時存在兩邊,但是以 PostgreSQL 為準。
這樣做的結果是:
- 開發效率保持不錯,新功能該快還是很快
- 成本控制住了,每月資料庫費用從 $800+ 降到 $300 左右
- 複雜查詢和報表功能順利實現
- 系統穩定性提升,資料完整性更有保障
當然,這個方案也不是沒有代價。我們需要維護兩套資料庫,確保資料同步的正確性,系統複雜度也增加了。
## 給你的建議
如果你也正在面臨同樣的選擇困擾,我想分享幾個建議:
**不要被「新技術」或「舊技術」的標籤影響判斷**。Firestore 很新很酷,但不代表它適合所有場景。SQL 很老很傳統,但不代表它過時了。重點是找到適合你當下需求的方案。
**先想清楚你的核心需求**。是開發速度重要,還是長期維護重要?是功能豐富重要,還是成本控制重要?沒有標準答案,只有適合你的答案。
**可以從小開始,逐步調整**。技術選型不是一錘子買賣,你可以先選一個容易開始的方案,在過程中學習和調整。我們就是這樣走過來的。
**團隊能力很重要**。如果團隊對 SQL 很熟悉,那選擇 PostgreSQL 的風險就比較低。如果大家都沒接觸過資料庫管理,那 Firestore 可能是更安全的選擇。
**混合使用是個不錯的選項**。不一定要非黑即白,可以讓不同的工具發揮各自的優勢。
最重要的是,**不要害怕做錯決定**。我們在 Firestore 上「浪費」的時間和金錢,其實讓我們學到了很多東西。現在的混合架構方案,如果沒有之前的經驗,我們也不可能設計得出來。
## 後記
寫這篇文章的時候,我們的產品已經穩定運行一年多了。月活用戶突破了 50 萬,系統依然穩定,成本也在可控範圍內。
最近又有新的挑戰了,我們在考慮要不要引入 Redis 來處理快取,要不要用 Elasticsearch 來改善搜尋功能。
技術選擇永遠不會停止,但是有了這次的經驗,我對做決定變得更有信心了。
你呢?你的資料庫選擇之路是怎樣的?遇到了什麼有趣的挑戰?歡迎在留言區分享你的故事。
---
*本文基於真實開發經驗撰寫,但為了保護隱私,部分細節已做調整。如果你正在面臨類似的技術選擇困擾,歡迎交流討論。*