转到控制台

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 手动定义和管理复合索引。如需详细了解如何创建和管理复合索引,请参阅管理索引

索引模式和查询范围

您可以采用不同方式配置单字段索引和复合索引,但两者都需要为索引配置索引模式和查询范围。

索引模式

定义索引时,您可以为每个已编入索引的字段选择索引模式。每个字段的索引模式都支持该字段上的特定查询子句。您可以从以下索引模式中进行选择:

索引模式 说明
升序 arrow_upward 支持在该字段上使用 <<===>=> 查询子句,并且支持根据此字段值按升序对结果进行排序。
降序 arrow_downward 支持在该字段上使用 <<===>=> 查询子句,并且支持根据此字段值按降序对结果进行排序。
数组包含 支持在字段上使用 array_contains 查询子句。

查询范围

每个索引都限定为集合或集合组。这就是索引的查询范围:

集合范围
默认情况下,Cloud Firestore 使用集合范围创建索引。这些索引支持从单个集合返回结果的查询。

集合组范围
集合组包括具有相同集合 ID 的所有集合。要运行从集合组返回已过滤或已排序结果的集合组查询,必须使用集合组范围创建相应的索引。

索引示例

由于 Cloud Firestore 会自动为您创建单字段索引,因此您的应用可以快速执行最基本的数据库查询。您可以使用单字段索引执行基于字段值和比较运算符(<<===>=>)的简单查询。对于数组字段,您可以使用单字段索引执行 array_contains 查询。

为便于说明,请从索引创建的角度查看以下示例。以下代码段在 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 会每个非数组字段更新一个升序单字段索引,为每个非数组字段更新一个降序单字段索引,并为数组字段更新一个包含单字段索引的数组。下表中的每一行代表单字段索引中的一个条目:

集合 编入索引的字段 查询范围
cities arrow_upward name 集合
cities arrow_upward state 集合
cities arrow_upward country 集合
cities arrow_upward capital 集合
cities arrow_upward population 集合
cities arrow_downward name 集合
cities arrow_downward state 集合
cities arrow_downward country 集合
cities arrow_downward capital 集合
cities arrow_downward population 集合
cities array-contains regions 集合

单字段索引支持的查询

使用这些自动创建的单字段索引,您可以运行如下所示的简单查询:

网站
citiesRef.where("state", "==", "CA")
citiesRef.where("population", "<", 100000)
citiesRef.where("name", ">=", "San Francisco")

您可以使用 array_contains 索引查询 regions 数组字段:

网页
citiesRef.where("regions", "array-contains", "west_coast")

您还可以创建基于等式 (==) 的复合查询:

网页
citiesRef.where("state", "==", "CO").where("name", "==", "Denver")
citiesRef.where("country", "==", "USA").where("capital", "==", false).where("state", "==", "CA").where("population", "==", 860000)

如果您需要运行使用了范围比较运算符(<<=>>=)的复合查询,或者您需要按照不同的字段进行排序,则您必须为该查询创建一个复合索引

复合索引支持的查询

Cloud Firestore 使用复合索引执行单字段索引不支持的复合查询。例如,您需要使用复合索引执行下列查询:

网页
citiesRef.where("country", "==", "USA").orderBy("population", "asc")
citiesRef.where("country", "==", "USA").where("population", "<", 3800000)
citiesRef.where("country", "==", "USA").where("population", ">", 690000)

这些查询需要使用下列复合索引。请注意,由于该查询为 country 字段使用了等式,因此该字段的索引模式可以为降序,也可以为升序。默认情况下,根据不等式子句中所使用的字段,不等式查询会使用升序排序。

集合 编入索引的字段 查询范围
cities arrow_upward(或 arrow_downward)country、arrow_upward population 集合

如果想要以降序运行相同的查询,您需要为 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")
集合 编入索引的字段 查询范围
cities arrow_upward country、arrow_upward population 集合
cities arrow_upward countryarrow_downward population 集合

如果您要将 array_contains 查询与其他子句组合使用,则还需要创建复合索引。

网页
citiesRef.where("regions", "array_contains", "east_coast")
         .where("capital", "==", true)
集合 编入索引的字段 查询范围
cities array-contains tags、arrow_upward(或 arrow_downward)capital 集合

集合组索引支持的查询

要演示使用集合组范围的索引,请假设您将 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" });

使用采用集合范围的以下单字段索引,您可以在单个城市中查询 park 地标:

集合 编入索引的字段 查询范围
地标 arrow_upward(或 arrow_downward)category 集合
网站
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")

现在,假设您对所有城市的 park 地标感兴趣。要对包含所有 landmarks 集合的集合组运行此查询,必须启用使用集合组范围的 landmarks 单字段索引:

集合 编入索引的字段 查询范围
地标 arrow_upward(或 arrow_downward)category 集合组

启用此索引后,您可以查询 landmarks 集合组:

网站
var landmarksGroupRef = db.collectionGroup("landmarks");

landmarksGroupRef.where("category", "==", "park")

要运行返回已过滤或已排序结果的集合组查询,必须启用使用集合组范围的相应单字段索引或复合索引。但是,不过滤或排序结果的集合组查询不需要任何其他索引定义。

例如,您可以在不启用其他索引的情况下运行以下集合组查询:

网站
db.collectionGroup("landmarks").get()

索引和价格

如果使用索引,您的应用会产生存储费用。如需详细了解如何计算索引的存储空间使用量,请参阅索引条目大小

充分利用索引合并功能

虽然 Cloud Firestore 会为每个查询都使用索引,但实际上没有必要为每个查询都创建一个索引。如果查询使用多个等式 (==) 子句和非必需的 orderBy 子句,Cloud Firestore 可以重复利用现有的索引。Cloud Firestore 可以将针对简单等式过滤条件创建的索引合并起来,构建运行更大的等式查询所需的复合索引。

通过找出可受益于索引合并的情况,您可以降低索引产生的费用。例如,假设一个餐厅评分应用有一个 restaurants 集合:

  • collections_bookmark restaurants

    • class burgerthyme

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

您可以为每个查询创建一个索引:

集合 编入索引的字段 查询范围
restaurants arrow_upward category、arrow_upward star_rating 集合
restaurants arrow_upward city、arrow_upward star_rating 集合
restaurants arrow_upward category、arrow_upward city、arrow_upward star_rating 集合
restaurants arrow_upward category、arrow_upward city、arrow_upward editors_pick、arrow_upward star_rating 集合

更好的方式是,充分利用 Cloud Firestore 为等式子句合并索引的功能,减少索引的数量:

集合 编入索引的字段 查询范围
restaurants arrow_upward category、arrow_upward star_rating 集合
restaurants arrow_upward city、arrow_upward star_rating 集合
restaurants arrow_upward editors_pick、arrow_upward star_rating 集合

此索引集不仅更小,还支持执行额外的查询:

网页
db.collection("restaurants").where("editors_pick", "==", true)
                            .orderBy("star_rating")

索引限制

索引存在以下限制。如需了解与配额和限制有关的全部信息,请参阅配额和限制

限制 详细信息
一个数据库的复合索引数量上限 200
一个数据库的单字段索引例外项的数量上限 200

每个文档的索引条目数量上限

40000

索引条目的数量是文档的以下各项数量的总和:

  • 单字段索引条目的数量
  • 复合索引条目的数量
索引条目的大小上限

7.5 KiB

要了解 Cloud Firestore 如何计算索引条目大小,请参阅索引条目大小

一个文档的索引条目的大小总和上限

8 MiB

总大小是文档的以下各项的大小总和:

  • 一个文档的单字段索引条目的大小总和
  • 一个文档的复合索引条目的大小总和
  • 编入索引的字段值的大小上限

    1500 字节

    超出 1500 字节的字段值会被截断。包含被截断的字段值的查询可能会返回不一致的结果。

    索引最佳做法

    对于大多数应用,您可以依靠自动索引和错误消息链接来管理索引。但是,在以下情况下,您可能需要添加单字段例外项:

    场景 说明
    大型字符串字段

    如果您的字符串字段通常包含不用于查询的长字符串值,您可以选择不将该字段编入索引来降低存储费用。

    向包含具有依序值的文档的集合高速写入数据

    如果您将在某个集合中的各文档之间依序递增或递减的字段(如时间戳)编入索引,则向该集合写入数据的最大速率为每秒 500 次写入。如果您不根据具有依序值的字段进行查询,则可以选择不将该字段编入索引来绕过此限制。

    例如,在具有高写入速率的 IoT 使用场景中,一个包含具有时间戳字段的文档的集合可能会达到每秒 500 次写入的限制。

    大型数组或映射字段

    大型数组或映射字段可能会达到每个文档 20000 个索引条目的限制。如果您没有根据大型数组或映射字段进行查询,则应该选择不将该字段编入索引。