跳至主要內容
ESC
GCP 核心服務 — 第 7/10 篇

GCP-112:Firestore 入門——NoSQL 文件資料庫完全指南

GCP-112

前言

應用需要一個不用管 schema、能即時同步、手機還能離線用的資料庫嗎?這種情境,Firestore 很適合。

這是 GCP 核心服務系列的第 7 課,我們來把 Firestore 的核心概念和實際操作搞懂。


什麼是 Firestore?

Firestore 是 GCP 的 NoSQL 文件資料庫(Document Database),資料以 JSON-like 文件形式儲存:

Firestore 資料庫
  └── 集合(Collection): users
        ├── 文件(Document): user-001
        │     ├── name: "Bobo"
        │     ├── email: "bobo@example.com"
        │     └── 子集合(Subcollection): orders
        │           ├── 文件: order-001
        │           └── 文件: order-002
        └── 文件(Document): user-002
              └── name: "Alice"

Firestore 是 Cloud Datastore 的下一代。原本的 Datastore 已經不是獨立產品了,現在改以 Firestore in Datastore mode 的形式繼續活著。


Native Mode vs Datastore Mode

建立 Firestore 資料庫時,你必須選擇模式:

特性Native ModeDatastore Mode
APIFirestore APIDatastore API
即時同步支援不支援
離線支援支援(Mobile/Web SDK)不支援
強一致性全面強一致性全面強一致性
適合場景Mobile/Web 應用伺服器端應用
App Engine 整合需要手動設定原生整合

能切換嗎?

只有資料庫是空的時候才能切換模式。一旦寫進去任何資料,模式就鎖死了。

建議

  • 新專案一律選 Native Mode——功能更完整
  • 只有既有 Datastore 應用才需要用 Datastore Mode

資料模型核心概念

文件(Document)

文件是 Firestore 的基本單位,類似 JSON 物件:

# Python 範例:新增文件
from google.cloud import firestore

db = firestore.Client()

# 自動生成 ID
doc_ref = db.collection("users").document()
doc_ref.set({
    "name": "Bobo",
    "email": "bobo@example.com",
    "age": 30,
    "tags": ["gcp", "cloud"],
    "address": {
        "city": "Taipei",
        "country": "Taiwan"
    }
})

# 指定 ID
db.collection("users").document("user-001").set({
    "name": "Alice"
})

重要限制

項目限制
文件大小上限1 MiB(1,048,576 bytes)
欄位值大小上限1 MiB - 89 bytes
子集合巢狀深度100 層
文件名稱大小6 KiB
Map/Array 巢狀深度20 層

集合(Collection)

集合是放文件的容器,不用事先建好。寫進第一個文件時它自動冒出來,刪掉最後一個文件時又自動消失。

子集合(Subcollection)

文件可以包含子集合,用來建立層級關係:

# 在 user-001 下建立 orders 子集合
db.collection("users").document("user-001") \
  .collection("orders").document("order-001").set({
    "item": "Cloud certification",
    "amount": 200
})

CRUD 操作

讀取

# 讀取單一文件
doc = db.collection("users").document("user-001").get()
if doc.exists:
    print(doc.to_dict())

# 查詢
query = db.collection("users") \
    .where("age", ">=", 25) \
    .where("tags", "array_contains", "gcp") \
    .order_by("age") \
    .limit(10)

for doc in query.stream():
    print(f"{doc.id}: {doc.to_dict()}")

更新

# 更新特定欄位(不影響其他欄位)
db.collection("users").document("user-001").update({
    "age": 31,
    "address.city": "Kaohsiung"  # 更新巢狀欄位
})

刪除

# 刪除文件
db.collection("users").document("user-001").delete()

# 刪除特定欄位
db.collection("users").document("user-001").update({
    "age": firestore.DELETE_FIELD
})

批次與交易

# 批次寫入(現已無 500 筆上限,改受單次提交約 10 MiB 大小限制;舊版曾限制 500 筆)
batch = db.batch()
for i in range(100):
    ref = db.collection("items").document(f"item-{i}")
    batch.set(ref, {"value": i})
batch.commit()

# 交易(讀取 + 寫入的原子操作)
@firestore.transactional
def transfer(transaction, from_ref, to_ref, amount):
    from_doc = from_ref.get(transaction=transaction)
    to_doc = to_ref.get(transaction=transaction)

    from_balance = from_doc.get("balance") - amount
    to_balance = to_doc.get("balance") + amount

    transaction.update(from_ref, {"balance": from_balance})
    transaction.update(to_ref, {"balance": to_balance})

transaction = db.transaction()
transfer(transaction,
         db.collection("accounts").document("A"),
         db.collection("accounts").document("B"),
         100)

即時監聽(Real-time Listeners)

Native Mode 的獨家功能——資料變更時自動推送通知:

# 監聽單一文件
def on_snapshot(doc_snapshot, changes, read_time):
    for doc in doc_snapshot:
        print(f"Changed: {doc.to_dict()}")

doc_ref = db.collection("users").document("user-001")
doc_watch = doc_ref.on_snapshot(on_snapshot)

# 監聽查詢結果
query_ref = db.collection("users").where("age", ">=", 25)
query_watch = query_ref.on_snapshot(on_snapshot)

# 停止監聽
doc_watch.unsubscribe()

即時監聽的運作原理

Firestore 的 changelog 伺服器會記錄所有資料變更。寫入流量一變大,changelog 就自動水平擴展到多台伺服器。用戶端連上之後,Firestore 會把符合查詢條件的變更主動推給它。


索引設計

自動索引

Firestore 會幫每個欄位自動建好單欄位索引,基本的等式和範圍查詢都罩得住。

複合索引

多欄位查詢需要手動建立複合索引:

# 這個查詢需要複合索引:(age ASC, name ASC)
query = db.collection("users") \
    .where("age", ">=", 25) \
    .order_by("age") \
    .order_by("name")
# 建立複合索引
gcloud firestore indexes composite create \
  --collection-group=users \
  --field-config=field-path=age,order=ascending \
  --field-config=field-path=name,order=ascending

索引限制

項目限制
複合索引數量(無帳單)200
複合索引數量(有帳單)1,000
單一文件的索引項目數40,000
複合索引最大欄位數100

安全規則 vs IAM

安全規則(Security Rules)

用於 Mobile/Web 用戶端的存取控制:

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // 用戶只能讀寫自己的資料
    match /users/{userId} {
      allow read, write: if request.auth != null
                         && request.auth.uid == userId;
    }

    // 公開讀取,只有管理員能寫入
    match /posts/{postId} {
      allow read: if true;
      allow write: if request.auth.token.admin == true;
    }
  }
}

IAM

用於伺服器端的存取控制:

# 授予 Service Account Firestore 讀寫權限
gcloud projects add-iam-policy-binding my-project \
  --member="serviceAccount:my-sa@my-project.iam.gserviceaccount.com" \
  --role="roles/datastore.user"

何時用哪個?

Mobile/Web App(直接從用戶端存取)→ Security Rules ✅
Server-side App(後端 API 伺服器)→ IAM ✅

Firestore Editions

特性StandardEnterprise
基本 CRUD支援支援
即時同步支援支援
備份標準備份標準備份
PITR(時間點還原)7 天7 天
向量搜尋不支援支援
全文搜尋不支援支援
MongoDB 相容不支援支援

多資料庫支援

Firestore 支援在同一個專案中建立多個資料庫(Named Databases):

# 建立額外的資料庫
gcloud firestore databases create \
  --database=analytics-db \
  --location=asia-east1 \
  --type=firestore-native
# 連線到特定資料庫
db = firestore.Client(database="analytics-db")

只有預設資料庫(default))享有免費層額度。


TTL(自動過期刪除)

Firestore TTL 讓你指定欄位作為過期時間,文件到期後自動刪除:

# 設定 TTL 欄位
gcloud firestore fields ttls update expiresAt \
  --collection-group=sessions \
  --enable-ttl
from datetime import datetime, timedelta

# 建立 30 天後自動過期的文件
db.collection("sessions").add({
    "userId": "user-001",
    "expiresAt": datetime.now() + timedelta(days=30)
})

定價與免費層

免費層(每天,僅限預設資料庫)

項目免費額度
文件讀取50,000 次/天
文件寫入20,000 次/天
文件刪除20,000 次/天
儲存空間1 GiB
對外流量10 GiB/月

超過免費層(美國區域)

項目費用
文件讀取$0.03 / 100,000 次
文件寫入$0.09 / 100,000 次
文件刪除$0.01 / 100,000 次
儲存空間$0.15 / GiB / 月

Firestore vs Cloud SQL vs Bigtable

ACE 考試最常考的 NoSQL 選型題:

特性FirestoreCloud SQLBigtable
類型文件型 NoSQL關聯式 SQL寬列型 NoSQL
資料模型JSON 文件表格(Row/Column)Key-Value 寬列
查詢語言Firestore QuerySQLRow Key Scan
擴展性自動水平擴展垂直擴展 + 讀取複本自動水平擴展
單筆大小1 MiB依欄位定義10 MB/cell100 MB/row
免費層50K 讀/天
最適合Mobile/Web App交易型系統時序/IoT/分析

選型公式

Mobile/Web App,需要即時同步和離線?
  → Firestore ✅

需要 SQL JOIN、ACID 交易、嚴格 schema?
  → Cloud SQL ✅

TB/PB 級時序資料、IoT 感測器、分析用?
  → Bigtable ✅

ACE 考試重點整理

必背知識點

  1. Firestore 有兩種模式:Native(推薦)和 Datastore,只有空資料庫能切換
  2. 文件大小上限 1 MiB,子集合深度上限 100 層
  3. 即時監聽是 Native Mode 獨有功能
  4. 安全規則用於 Mobile/Web,IAM 用於伺服器端
  5. 免費層:50K 讀取/天、20K 寫入/天
  6. 多資料庫支援:同一專案可建立多個資料庫
  7. 自動索引:單欄位自動建索引,多欄位需手動建複合索引

常見陷阱題

Q:Firestore 和 Datastore 是兩個不同的產品嗎? A:不是。Datastore 已經併入 Firestore,以 Firestore in Datastore mode 的形式存在。新專案請選 Native Mode。

Q:Firestore 適合儲存 10MB 的檔案嗎? A:不適合。文件大小上限 1 MiB。大型檔案應存在 Cloud Storage,Firestore 只存 metadata 和 GCS 路徑。

Q:Mobile App 直接存取 Firestore,要用 IAM 還是 Security Rules? A:Security Rules。IAM 用於伺服器端,Security Rules 才能做到用戶級別的存取控制。

Q:需要即時同步資料給多個用戶端,用什麼服務? A:**Firestore(Native Mode)**的即時監聽功能。


總結

Firestore 是 GCP 裡最靈活的 NoSQL 資料庫,重點整理一下:

  • 兩種模式:Native(推薦)vs Datastore,只有空庫能切換
  • 資料模型:集合 → 文件 → 子集合,文件上限 1 MiB
  • 即時監聽:Native Mode 獨有,資料變更自動推送
  • 安全控制:Mobile/Web 用 Security Rules,Server 用 IAM
  • 免費層:50K 讀取/天、20K 寫入/天、1 GiB 儲存
  • 選型:Mobile App → Firestore;SQL → Cloud SQL;大數據 → Bigtable

下一課 GCP-113:Bigtable 深度解析,來看 Google 那套 PB 級的寬列型 NoSQL 資料庫。

資料庫選型系列

課程服務適合場景
GCP-108Cloud SQL中小型 OLTP、傳統 SQL 應用
本課 GCP-112FirestoreMobile/Web App、即時同步
GCP-113BigtablePB 級 IoT/時序、低延遲高吞吐
ACE-211BigQueryPB 級分析、BI 報表
ACE-213Spanner全球規模 OLTP、強一致性
GCP-115Memorystore微秒級快取、Session 管理

📖 完整比較:想一次看懂所有資料庫差異?參考 GCP 資料庫選型完全指南

GCP 核心服務 — 7/10 完成 查看系列全覽 →

留言討論

徽章解鎖!