Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

構建您的數據庫

本指南涵蓋了數據架構中的一些關鍵概念以及在 Firebase 實時數據庫中構建 JSON 數據的最佳實踐。

構建一個結構合理的數據庫需要相當多的深謀遠慮。最重要的是,您需要計劃如何保存和稍後檢索數據,以使該過程盡可能簡單。

數據的結構:它是一個 JSON 樹

所有 Firebase 實時數據庫數據都存儲為 JSON 對象。您可以將數據庫視為雲託管的 JSON 樹。與 SQL 數據庫不同,它沒有表或記錄。當您向 JSON 樹添加數據時,它會成為現有 JSON 結構中具有關聯鍵的節點。您可以提供自己的鑰匙,如用戶ID或語義的名稱,也可以使用你的對提供POST請求

如果你創建自己的密鑰,它們必須是UTF-8編碼,可以最多768個字節,並不能包含.$#[]/ ,或ASCII控制字符0-31或127不能在值使用ASCII控制字符本身,無論是。

例如,考慮一個允許用戶存儲基本配置文件和聯繫人列表的聊天應用程序。一個典型的用戶簡檔位於一個路徑,如/users/$uid 。用戶alovelace可能有一個數據庫條目看起來是這樣的:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

儘管數據庫使用 JSON 樹,但存儲在數據庫中的數據可以表示為與可用 JSON 類型相對應的某些本機類型,以幫助您編寫更易於維護的代碼。

數據結構的最佳實踐

避免嵌套數據

由於 Firebase 實時數據庫允許嵌套深度高達 32 級的數據,您可能會認為這應該是默認結構。但是,當您在數據庫中的某個位置獲取數據時,您還會檢索其所有子節點。此外,當您授予某人對數據庫中某個節點的讀或寫訪問權限時,您還授予他們對該節點下所有數據的訪問權限。因此,在實踐中,最好使您的數據結構盡可能平坦。

有關嵌套數據為何不好的示例,請考慮以下多重嵌套結構:

{
  // This is a poorly nested data architecture, because iterating the children
  // of the "chats" node to get a list of conversation titles requires
  // potentially downloading hundreds of megabytes of messages
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "messages": {
        "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
        "m2": { ... },
        // a very long list of messages
      }
    },
    "two": { ... }
  }
}

使用這種嵌套設計,遍歷數據變得有問題。例如,上市聊天對話的標題需要整個chats樹,包含所有成員和消息,下載到客戶端。

扁平化數據結構

如果將數據拆分為單獨的路徑(也稱為非規範化),則可以根據需要在單獨的調用中高效下載。考慮這個扁平結構:

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // conversation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

現在可以通過每次對話僅下載幾個字節來遍歷房間列表,快速獲取元數據以在 UI 中列出或顯示房間。消息可以單獨獲取並在它們到達時顯示,從而使 UI 保持響應速度和速度。

創建可擴展的數據

在構建應用程序時,下載列表的子集通常會更好。如果列表包含數千條記錄,這尤其常見。當這種關係是靜態的和單向的時,您可以簡單地將子對象嵌套在父對像下。

有時,這種關係更加動態,或者可能需要對這些數據進行非規範化。很多時候,你可以通過查詢檢索數據的子集,在討論非規範化的數據檢索數據

但即使這樣也可能是不夠的。例如,考慮用戶和組之間的雙向關係。用戶可以屬於一個組,而組包含一個用戶列表。當需要決定用戶屬於哪個組時,事情就變得複雜了。

我們需要一種優雅的方式來列出用戶所屬的組並僅獲取這些組的數據。組的索引可以幫助一個很大的位置:

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

您可能會注意到,通過將關係存儲在 Ada 的記錄下和組下,這會復制一些數據。現在alovelace是一組下建立索引, techpioneers在阿達的配置文件中列出。所以要從組中刪除 Ada,它必須在兩個地方更新。

這是雙向關係的必要冗餘。它允許您快速有效地獲取 Ada 的成員資格,即使在用戶或組列表擴展到數百萬或實時數據庫安全規則阻止訪問某些記錄時也是如此。

這種方法,通過列舉的ID作為鍵並將其值設置為true反轉數據,進行檢查的關鍵就這麼簡單讀/users/$uid/groups/$group_id並檢查它是否null 。索引比查詢或掃描數據更快,效率更高。

下一步