索引是數據庫性能的重要因素。與將書中的主題映射到頁碼的書籍索引非常相似,數據庫索引將數據庫中的項目映射到它們在數據庫中的位置。當您向數據庫發送查詢時,數據庫可以使用索引來快速查找您請求的項目的位置。
本頁面介紹 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)
收藏 | 已索引的字段 | 查詢範圍 |
---|---|---|
城市 | 數組包含標籤, | (或 )大寫收藏 |
集合組索引支持的查詢
為了演示具有集合組範圍的索引,假設您向某些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()
索引條目
您的項目配置的索引和文檔的結構決定了文檔的索引條目數。索引條目計入索引條目計數限制。
以下示例演示了文檔的索引條目。
文件
/cities/SF
city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]
單字段索引
- 城市名稱 ASC
- 城市名稱 DESC
- 夏季 ASC 溫度
- 溫度.夏季 DESC
- 冬季ASC溫度
- 溫度.冬季 DESC
- Neighborhoods 數組包含(ASC 和 DESC)
綜合指數
- city_name ASC,社區數組
- city_name DESC,街區 ARRAY
索引條目
此索引配置會生成以下 18 個文檔索引條目:
指數 | 索引數據 |
---|---|
單字段索引條目 | |
城市名稱 ASC | city_name:“舊金山” |
城市名稱 DESC | city_name:“舊金山” |
夏季 ASC 溫度 | 夏季溫度:67 |
溫度.夏季 DESC | 夏季溫度:67 |
冬季ASC溫度 | 冬季溫度:55 |
溫度.冬季 DESC | 冬季溫度:55 |
Neighborhoods 數組包含 ASC | 社區:“使命” |
Neighborhoods 數組包含 DESC | 社區:“使命” |
Neighborhoods 數組包含 ASC | 街區:“市中心” |
Neighborhoods 數組包含 DESC | 街區:“市中心” |
Neighborhoods 數組包含 ASC | 社區:“碼頭” |
Neighborhoods 數組包含 DESC | 社區:“碼頭” |
綜合索引條目 | |
city_name ASC,社區數組 | city_name:“舊金山”,街區:“Mission” |
city_name ASC,社區數組 | city_name:“舊金山”,街區:“市中心” |
city_name ASC,社區數組 | city_name:“舊金山”,街區:“碼頭” |
city_name DESC,街區 ARRAY | city_name:“舊金山”,街區:“Mission” |
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 | 收藏 |
餐館 | 類別、 城市、 星級 | 收藏 |
餐館 | 類別、 城市、 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
錯誤)的更多信息,請查看故障排除頁面。