获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

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-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-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

现在,想象一下这个应用程序使用如下查询。请注意,该应用程序对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

总共允许 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 个索引条目的限制。如果您不是基于大型数组或映射字段进行查询,则应将其免除索引。