索引是影響數據庫性能的重要因素。與將書中的主題映射到頁碼的書籍索引非常相似,數據庫索引將數據庫中的項目映射到它們在數據庫中的位置。當您向數據庫發送查詢時,數據庫可以使用索引快速查找您請求的項目的位置。
本頁面介紹了 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 查詢子句,支持根據該字段值對結果進行升序排序。 |
降序 | 支持字段上的< , <= , == , >= , > , != , in , not-in 查詢子句,支持根據該字段值對結果進行降序排序。 |
數組-包含 | 支持array-contains 和array-contains-any 字段上的查詢子句。 |
查詢範圍
每個索引的範圍都是一個集合或一個集合組。這稱為索引的查詢範圍:
- 收集範圍
- Cloud Firestore 默認創建具有集合範圍的索引。這些索引支持從單個集合返回結果的查詢。
- 集合組範圍
- 集合組包括具有相同集合 ID 的所有集合。要運行從集合組返回過濾或排序結果的集合組查詢,您必須創建一個具有集合組範圍的相應索引。
默認排序和__name__
字段
除了按為每個字段指定的索引模式(升序或降序)對文檔進行排序外,索引還按每個文檔的__name__
字段應用最終排序。 __name__
字段的值設置為完整文檔路徑。這意味著結果集中具有相同字段值的文檔按文檔路徑排序。
默認情況下, __name__
字段的排序方向與索引定義中最後一個排序字段的方向相同。例如:
收藏 | 字段索引 | 查詢範圍 |
---|---|---|
城市 | __name__ | 名稱, 收藏 |
城市 | __name__ | 狀態, 收藏 |
城市 | __name__ | 國家, 人口, 收藏 |
要按非默認__name__
方向對結果進行排序,您需要創建該索引。
索引示例
通過自動為您創建單字段索引,Cloud Firestore 讓您的應用程序能夠快速支持最基本的數據庫查詢。單字段索引允許您根據字段值和比較器<
、 <=
、 ==
、 >=
、 >
和in
執行簡單查詢。對於數組字段,它們允許您執行array-contains
和array-contains-any
查詢。
為了說明,請從索引創建的角度檢查以下示例。以下代碼片段在cities
集合中創建了一些city
文檔,並為每個文檔設置了name
、 state
、 country
、 capital
、 population
和tags
字段:
網絡
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-contains
或array-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)
收藏 | 字段索引 | 查詢範圍 |
---|---|---|
城市 | array-contains標籤, | (或 )資本收藏 |
集合組索引支持的查詢
為了演示具有集合組範圍的索引,假設您將landmarks
子集合添加到一些city
文檔中:
網絡
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()
索引條目
您的項目配置的索引和文檔的結構決定了文檔的索引條目數。索引條目計入索引條目計數限制。
以下示例演示文檔的索引條目。
文檔
/cities/SF
city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]
單字段索引
- 城市名稱 ASC
- city_name 描述
- 溫度.summer ASC
- 溫度.summer DESC
- 溫度.winter ASC
- 溫度.winter DESC
- neighborhoods 數組包含(ASC 和 DESC)
複合指標
- city_name ASC,社區 ARRAY
- city_name DESC,社區 ARRAY
索引條目
此索引配置為文檔生成以下 18 個索引條目:
指數 | 索引數據 |
---|---|
單字段索引條目 | |
城市名稱 ASC | city_name:“舊金山” |
city_name 描述 | city_name:“舊金山” |
溫度.summer ASC | 夏季溫度:67 |
溫度.summer DESC | 夏季溫度:67 |
溫度.winter ASC | 溫度。冬季:55 |
溫度.winter DESC | 溫度。冬季:55 |
neighborhoods 數組包含 ASC | 社區:“任務” |
neighborhoods 數組包含 DESC | 社區:“任務” |
neighborhoods 數組包含 ASC | 街區:“市中心” |
neighborhoods 數組包含 DESC | 街區:“市中心” |
neighborhoods 數組包含 ASC | 社區:“碼頭” |
neighborhoods 數組包含 DESC | 社區:“碼頭” |
複合索引條目 | |
city_name ASC,社區 ARRAY | city_name:“舊金山”,社區:“使命” |
city_name ASC,社區 ARRAY | city_name:“舊金山”,社區:“市區” |
city_name ASC,社區 ARRAY | city_name:“舊金山”,社區:“碼頭” |
city_name DESC,社區 ARRAY | city_name:“舊金山”,社區:“使命” |
city_name DESC,社區 ARRAY | city_name:“舊金山”,社區:“市區” |
city_name DESC,社區 ARRAY | city_name:“舊金山”,社區:“碼頭” |
指數和定價
索引會增加應用程序的存儲成本。有關如何計算索引存儲大小的更多信息,請參閱索引條目大小。
利用索引合併
儘管 Cloud Firestore 對每個查詢都使用一個索引,但它不一定每個查詢都需要一個索引。對於具有多個相等 ( ==
) 子句和可選的orderBy
子句的查詢,Cloud Firestore 可以重複使用現有索引。 Cloud Firestore 可以合併簡單相等性過濾器的索引,以構建更大的相等性查詢所需的複合索引。
您可以通過確定可以利用索引合併的情況來降低索引成本。例如,假設餐廳評級應用程序有一個restaurants
集合:
漢堡百里香
name : "Burger Thyme"
category : "burgers"
city : "San Francisco"
editors_pick : true
star_rating : 4
現在,想像一下這個應用程序使用如下查詢。請注意,該應用程序對category
、 city
和editors_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")
您可以為每個查詢創建一個索引:
收藏 | 字段索引 | 查詢範圍 |
---|---|---|
餐館 | 類別, star_rating | 收藏 |
餐廳 | 城市, star_rating | 收藏 |
餐館 | 類別, 城市, star_rating | 收藏 |
餐廳 | 類別, 城市, editors_pick, star_rating | 收藏 |
作為更好的解決方案,您可以利用 Cloud Firestore 為等式子句合併索引的功能來減少索引的數量:
收藏 | 字段索引 | 查詢範圍 |
---|---|---|
餐廳 | 類別, star_rating | 收藏 |
餐廳 | 城市, star_rating | 收藏 |
餐館 | editors_pick, star_rating | 收藏 |
這組索引不僅更小,而且還支持額外的查詢:
網絡
db.collection("restaurants").where("editors_pick", "==", true) .orderBy("star_rating")
索引限制
以下限制適用於索引。有關所有配額和限制,請參閱配額和限制。
限制 | 細節 |
---|---|
一個數據庫的最大復合索引數 | 200 您可以聯繫支持人員請求增加此限制。 |
一個數據庫的最大單字段配置數 | 200 總共允許 200 個字段級配置。一個字段配置可以包含同一字段的多個配置。例如,單字段索引豁免和同一字段上的 TTL 策略計為一個字段配置,以達到限制。 |
每個文檔的最大索引條目數 | 40,000 索引條目數是文檔的以下各項的總和:
要了解 Cloud Firestore 如何將文檔和一組索引轉換為索引條目,請參閱此索引條目計數示例。 |
複合索引中的最大字段數 | 100 |
索引條目的最大大小 | 7.5 KB 要查看 Cloud Firestore 如何計算索引條目大小,請參閱索引條目大小。 |
文檔索引條目大小的最大總和 | 8 字節 文檔的總大小是以下各項的總和: |
索引字段值的最大大小 | 1500 字節 超過 1500 字節的字段值將被截斷。涉及截斷字段值的查詢可能會返回不一致的結果。 |
索引最佳實踐
對於大多數應用程序,您可以依靠自動索引和錯誤消息鏈接來管理您的索引。但是,您可能希望在以下情況下添加單字段豁免:
案件 | 描述 |
---|---|
大字符串字段 | 如果您有一個字符串字段,該字段通常包含您不用於查詢的長字符串值,則可以通過免除該字段的索引來降低存儲成本。 |
對包含具有順序值的文檔的集合的高寫入率 | 如果您索引一個在集合中的文檔之間按順序增加或減少的字段,例如時間戳,那麼對該集合的最大寫入速率是每秒 500 次寫入。如果您不基於具有順序值的字段進行查詢,則可以免除該字段的索引以繞過此限制。 例如,在具有高寫入率的 IoT 用例中,包含具有時間戳字段的文檔的集合可能接近每秒 500 次寫入限制。 |
TTL字段 | 如果您使用TTL(生存時間)策略,請注意 TTL 字段必須是時間戳。 TTL 字段的索引在默認情況下處於啟用狀態,並且會影響較高流量速率下的性能。作為最佳實踐,為您的 TTL 字段添加單字段豁免。 |
大數組或映射字段 | 大型數組或映射字段可能接近每個文檔 40,000 個索引條目的限制。如果您不是基於大型數組或映射字段進行查詢,則應將其免除索引。 |
有關如何解決索引問題(索引扇出、 INVALID_ARGUMENT
錯誤)的更多信息,請查看故障排除頁面。