Cloud Firestore 中的索引類型

索引是影響數據庫性能的重要因素。就像一本書的索引將書中的主題映射到頁碼一樣,數據庫索引將數據庫中的項目映射到它們在數據庫中的位置。當您向數據庫發送查詢時,數據庫可以使用索引來快速查找您請求的項目的位置。

本頁面介紹了 Cloud Firestore 使用的兩種類型的索引,單字段索引復合索引

每個查詢背後的索引

如果查詢不存在索引,大多數數據庫會逐項抓取其內容,這是一個緩慢的過程,隨著數據庫的增長,速度會更加緩慢。 Cloud Firestore 通過對所有查詢使用索引來保證高查詢性能。因此,查詢性能取決於結果集的大小,而不是數據庫中的項目數。

更少的索引管理,更多的應用程序開發

Cloud Firestore 包含可減少您花在管理索引上的時間的功能。系統會自動為您創建最基本查詢所需的索引。在您使用和測試應用時,Cloud Firestore 可幫助您識別和創建應用所需的其他索引

索引類型

Cloud Firestore 使用兩種類型的索引:單字段復合索引。除了索引字段的數量之外,單字段索引和復合索引在管理它們的方式上也有所不同。

單字段索引

單字段索引存儲包含特定字段的集合中所有文檔的排序映射。單字段索引中的每個條目記錄特定字段的文檔值以及文檔在數據庫中的位置。 Cloud Firestore 使用這些索引來執行許多基本查詢。您可以通過配置數據庫的自動索引設置和索引豁免來管理單字段索引。

自動索引

默認情況下,Cloud Firestore 會自動為文檔中的每個字段和地圖中的每個子字段維護單字段索引。 Cloud Firestore 對單字段索引使用以下默認設置:

  • 對於每個非數組和非映射字段,Cloud Firestore 定義了兩個集合範圍的單字段索引,一個是升序索引,一個是降序索引。

  • 對於每個地圖字段,Cloud Firestore 為地圖中的每個非數組和非地圖子字段創建一個集合範圍升序索引和一個降序索引。

  • 對於文檔中的每個數組字段,Cloud Firestore 都會創建並維護一個集合範圍的數組包含索引。

  • 默認情況下不維護具有集合組範圍的單字段索引。

單字段索引豁免

您可以通過創建單字段索引豁免來從自動索引設置中豁免字段。索引豁免會覆蓋數據庫範圍的自動索引設置。豁免可以啟用單字段索引,否則您的自動索引設置會禁用或禁用自動索引本來會啟用的單字段索引。對於豁免可能有用的情況,請參閱索引最佳實踐

如果您為地圖字段創建單字段索引豁免,則地圖的子字段會繼承這些設置。但是,您可以為特定子字段定義單字段索引豁免。如果刪除子字段的豁免,則子字段將繼承其父項的豁免設置(如果存在)或數據庫範圍的設置(如果不存在父項豁免)。

要創建和管理單字段索引豁免,請參閱在 Cloud Firestore 中管理索引

綜合指數

複合索引根據要索引的字段的有序列表存儲集合中所有文檔的排序映射。

Cloud Firestore 使用複合索引來支持單字段索引尚不支持的查詢。

Cloud Firestore 不會像為單字段索引那樣自動創建複合索引,因為可能存在大量字段組合。相反,Cloud Firestore 可幫助您在構建應用時識別和創建所需的複合索引

如果您在未先創建所需索引的情況下嘗試上述查詢,Cloud Firestore 會返回一條錯誤消息,其中包含您可以點擊以創建缺少的索引的鏈接。每當您嘗試索引不支持的查詢時,都會發生這種情況。您還可以使用控制台或使用Firebase CLI手動定義和管理複合索引。有關創建和管理複合索引的更多信息,請參閱管理索引

索引模式和查詢範圍

您以不同的方式配置單字段索引和復合索引,但兩者都要求您為索引配置索引模式和查詢範圍。

索引模式

定義索引時,為每個索引字段選擇索引模式。每個字段的索引模式都支持該字段上的特定查詢子句。您可以從以下索引模式中進行選擇:

索引模式描述
上升支持< , <= , == , >= , > , != , in , not-in , 查詢子句,支持根據該字段值升序排列結果。
下降支持字段上的<<===>=>!=innot-in查詢子句,並支持根據該字段值對結果進行降序排序。
數組包含支持字段上的array-containsarray-contains-any查詢子句。

查詢範圍

每個索引的範圍都是一個集合或一個集合組。這稱為索引的查詢範圍:

收藏範圍
默認情況下,Cloud Firestore 會創建具有集合範圍的索引。這些索引支持從單個集合返回結果的查詢。

集合組範圍
集合組包括具有相同集合 ID 的所有集合。要運行從集合組返回過濾或排序結果的集合組查詢,您必須創建具有集合組範圍的相應索引。

索引示例

通過自動為您創建單字段索引,Cloud Firestore 允許您的應用程序快速支持最基本的數據庫查詢。單字段索引允許您基於字段值和比較器<<===>=>in執行簡單查詢。對於數組字段,它們允許您執行array-containsarray-contains-any查詢。

為了說明這一點,請從索引創建的角度檢查以下示例。以下代碼片段在cities集合中創建了一些city文檔,並為每個文檔設置了namestatecountrycapitalpopulationtags字段:

網絡
var citiesRef = db.collection("cities");

citiesRef.doc("SF").set({
    name: "San Francisco", state: "CA", country: "USA",
    capital: false, population: 860000,
    regions: ["west_coast", "norcal"] });
citiesRef.doc("LA").set({
    name: "Los Angeles", state: "CA", country: "USA",
    capital: false, population: 3900000,
    regions: ["west_coast", "socal"] });
citiesRef.doc("DC").set({
    name: "Washington, D.C.", state: null, country: "USA",
    capital: true, population: 680000,
    regions: ["east_coast"] });
citiesRef.doc("TOK").set({
    name: "Tokyo", state: null, country: "Japan",
    capital: true, population: 9000000,
    regions: ["kanto", "honshu"] });
citiesRef.doc("BJ").set({
    name: "Beijing", state: null, country: "China",
    capital: true, population: 21500000,
    regions: ["jingjinji", "hebei"] });

假設默認的自動索引設置,Cloud Firestore 會為每個非數組字段更新一個升序單字段索引,每個非數組字段一個降序單字段索引,以及一個包含數組字段的單字段索引。下表中的每一行代表單字段索引中的一個條目:

收藏字段索引查詢範圍
城市名稱收藏
城市狀態收藏
城市國家收藏
城市資本收藏
城市人口收藏
城市名稱收藏
城市狀態收藏
城市國家收藏
城市資本收藏
城市人口收藏
城市array-contains區域收藏

單字段索引支持的查詢

使用這些自動創建的單字段索引,您可以運行如下簡單查詢:

網絡
const stateQuery = citiesRef.where("state", "==", "CA");
const populationQuery = citiesRef.where("population", "<", 100000);
const nameQuery = citiesRef.where("name", ">=", "San Francisco");

您還可以創建in和復合相等 ( == ) 查詢:

網絡
citiesRef.where('country', 'in', ["USA", "Japan", "China"])

// Compound equality queries
citiesRef.where("state", "==", "CO").where("name", "==", "Denver")
citiesRef.where("country", "==", "USA")
         .where("capital", "==", false)
         .where("state", "==", "CA")
         .where("population", "==", 860000)

如果您需要運行使用範圍比較( <<=>>= )的複合查詢,或者如果您需要按不同的字段排序,則必須為該查詢創建複合索引

array-contains索引允許您查詢regions數組字段:

網絡
citiesRef.where("regions", "array-contains", "west_coast")
// array-contains-any and array-contains use the same indexes
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])

複合索引支持的查詢

Cloud Firestore 使用複合索引來支持單字段索引尚不支持的複合查詢。例如,您需要為以下查詢創建一個複合索引:

網絡
citiesRef.where("country", "==", "USA").orderBy("population", "asc")
citiesRef.where("country", "==", "USA").where("population", "<", 3800000)
citiesRef.where("country", "==", "USA").where("population", ">", 690000)
// in and == clauses use the same index
citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)

這些查詢需要下面的複合索引。由於查詢對country字段使用相等( ==in ),因此您可以為此字段使用升序或降序索引模式。默認情況下,不等式子句根據不等式子句中的字段應用升序排序。

收藏索引的字段查詢範圍
城市 (或 )國家, 人口收藏

要運行相同的查詢但使用降序排序,您需要一個額外的複合索引以用於population的降序:

網絡
citiesRef.where("country", "==", "USA").orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", "<", 3800000)
         .orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", ">", 690000)
         .orderBy("population", "desc")

citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)
         .orderBy("population", "desc")
收藏索引的字段查詢範圍
城市國家, 人口收藏
城市國家, 人口收藏

您還需要創建一個複合索引以將array-containsarray-contains-any查詢與附加子句結合起來。

網絡
citiesRef.where("regions", "array-contains", "east_coast")
         .where("capital", "==", true)

// array-contains-any and array-contains use the same index
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])
         .where("capital", "==", true)
收藏索引的字段查詢範圍
城市數組包含標籤, (或 )大寫收藏

集合組索引支持的查詢

為了演示具有集合組範圍的索引,假設您向某些city文檔添加了landmarks子集合:

網絡
var citiesRef = db.collection("cities");

citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Bridge",
    category : "bridge" });
citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Park",
    category : "park" });

citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Gallery of Art",
    category : "museum" });
citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Mall",
    category : "park" });

使用以下帶有集合範圍的單字段索引,您可以根據category字段查詢單個城市的landmarks集合:

收藏索引的字段查詢範圍
地標 (或 )類別收藏
網絡
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")
citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])

現在,假設您有興趣查詢所有城市的地標。要在包含所有landmarks集合的集合組上運行此查詢,您必須啟用具有集合組範圍的landmarks單字段索引:

收藏索引的字段查詢範圍
地標 (或 )類別集合組

啟用此索引後,您可以查詢landmarks集合組:

網絡
var landmarksGroupRef = db.collectionGroup("landmarks");

landmarksGroupRef.where("category", "==", "park")
landmarksGroupRef.where("category", "in", ["park", "museum"])

要運行返回過濾或排序結果的集合組查詢,您必須啟用相應的具有集合組範圍的單字段或複合索引。但是,不過濾或排序結果的集合組查詢不需要任何額外的索引定義。

例如,您可以在不啟用附加索引的情況下運行以下集合組查詢:

網絡
db.collectionGroup("landmarks").get()

索引條目

您的項目配置的索引以及文檔的結構都會影響文檔的索引條目,最終計入索引條目計數限制

這裡有一個例子來說明。

文檔

name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]

單字段索引

  • (自動)名稱 ASC & DESC
  • (自動)溫度 ASC & DESC
  • (自動)鄰域數組包含

綜合指數

  • 名稱 ASC,社區 ASC
  • 名稱 DESC,街區 ASC

結果索引條目

此索引配置會為文檔生成以下 12 個索引條目:

指數入口
名稱 ASC & DESC名稱:“舊金山”
溫度 ASC 和 DESC溫度.夏季:67
溫度 ASC 和 DESC溫度。冬天:55
社區數組包含社區:“使命”
社區數組包含街區:“市中心”
社區數組包含社區:“碼頭”
名稱 ASC,社區 ASC名稱:“舊金山”,街區:“使命”
名稱 ASC,社區 ASC名稱:“舊金山”,街區:“市中心”
名稱 ASC,社區 ASC名稱:“舊金山”,街區:“碼頭”
名稱 DESC,街區 ASC名稱:“舊金山”,街區:“使命”
名稱 DESC,街區 ASC名稱:“舊金山”,街區:“市中心”
名稱 DESC,街區 ASC名稱:“舊金山”,街區:“碼頭”

指數和定價

索引會增加應用程序的存儲成本。有關如何計算索引存儲大小的更多信息,請參閱索引條目大小

利用索引合併

儘管 Cloud Firestore 對每個查詢都使用一個索引,但它並不一定需要每個查詢一個索引。對於具有多個相等 ( == ) 子句和可選的orderBy子句的查詢,Cloud Firestore 可以重複使用現有索引。 Cloud Firestore 可以合併簡單等式過濾器的索引,以構建更大的等式查詢所需的複合索引。

您可以通過確定可以利用索引合併的情況來降低索引成本。例如,想像一個餐廳評級應用程序的restaurants集合:

  • 餐廳

    • 漢堡百里香

      name : "Burger Thyme"
      category : "burgers"
      city : "San Francisco"
      editors_pick : true
      star_rating : 4

現在,想像一下這個應用程序使用如下查詢。請注意,該應用程序對categorycityeditors_pick使用相等子句的組合,同時始終按升序排序star_rating

網絡
db.collection("restaurants").where("category", "==", "burgers")
                            .orderBy("star_rating")

db.collection("restaurants").where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==" "San Francisco")
                            .where("editors_pick", "==", true )
                            .orderBy("star_rating")

您可以為每個查詢創建一個索引:

收藏索引的字段查詢範圍
餐館類別, 收藏
餐館城市, 收藏
餐館類別, 城市, 收藏
餐館類別, 城市, editors_pick, 收藏

作為更好的解決方案,您可以利用 Cloud Firestore 為相等子句合併索引的能力來減少索引數量:

收藏索引的字段查詢範圍
餐館類別, 收藏
餐館城市, 收藏
餐館 editors_pick, 收藏

這組索引不僅更小,還支持額外的查詢:

網絡
db.collection("restaurants").where("editors_pick", "==", true)
                            .orderBy("star_rating")

索引限制

以下限制適用於索引。有關所有配額和限制,請參閱配額和限制

限制細節
數據庫的最大復合索引數200
數據庫的最大單字段索引豁免數200

每個文檔的最大索引條目數

40,000

索引條目的數量是文檔以下各項的總和:

  • 單字段索引條目數
  • 複合索引條目數

要了解 Cloud Firestore 如何將文檔和一組索引轉換為索引條目,請參閱此索引條目計數示例

索引條目的最大大小

7.5 KB

要了解 Cloud Firestore 如何計算索引條目大小,請參閱索引條目大小

文檔索引條目大小的最大總和

8 兆字節

總大小是文檔以下各項的總和:

  • 文檔的單字段索引條目大小的總和
  • 文檔的複合索引條目大小的總和
  • 索引字段值的最大大小

    1500 字節

    超過 1500 字節的字段值將被截斷。涉及截斷字段值的查詢可能會返回不一致的結果。

    索引最佳實踐

    對於大多數應用程序,您可以依靠自動索引和錯誤消息鏈接來管理您的索引。但是,您可能希望在以下情況下添加單字段豁免:

    案件描述
    大字符串字段

    如果您有一個字符串字段通常包含您不用於查詢的長字符串值,您可以通過免除該字段的索引來降低存儲成本。

    對包含具有順序值的文檔的集合的高寫入率

    如果您索引一個在集合中的文檔之間按順序增加或減少的字段(如時間戳),則對集合的最大寫入速率為每秒 500 次寫入。如果您不根據具有順序值的字段進行查詢,則可以使該字段免於索引以繞過此限制。

    例如,在具有高寫入率的 IoT 用例中,包含具有時間戳字段的文檔的集合可能接近每秒 500 次寫入的限制。

    大型數組或映射字段

    大型數組或映射字段可以接近每個文檔 40,000 個索引條目的限制。如果您不是基於大型數組或映射字段進行查詢,則應將其排除在索引之外。