最佳做法

在构建使用 Firestore 的应用时,您可以快速参考本页面中列出的最佳做法。

数据库位置

创建数据库实例时,请选择距离您的用户最近的数据库位置和计算资源。 远距离网络跃点更容易出错,并会增加查询延迟时间。

若想最大限度地提高您的应用的可用性和耐用性,请选择一个多区域位置,并将关键计算资源放入至少两个区域中。

如果您的应用对延迟较敏感,或者您想要与其他 GCP 资源托管在同一位置,请选择一个单区域位置,以便降低费用并缩短写入延迟。

文档 ID

  • 不要使用 ... 作为文档 ID。
  • 避免在文档 ID 中使用正斜杠 /
  • 不要使用单调递增的文档 ID,例如:

    • Customer1, Customer2, Customer3, ...
    • Product 1, Product 2, Product 3, ...

    此类顺序 ID 可能会产生热点问题,从而增加延迟时间。

字段名称

  • 避免在字段名称中使用以下字符,因为它们需要额外的转义:

    • 句点 .
    • 左中括号 [
    • 右中括号 ]
    • 星号 *
    • 反引号 `

索引

  • 避免使用太多索引。过多的索引会增加写入延迟时间,并会增加索引条目的存储费用

  • 请注意,具有单调递增值的索引字段(如时间戳)可能会产生热点问题,因而会增加具有高读/写速率的应用的延迟时间。

索引例外项

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

场景 说明
大型字符串字段

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

向所含文档具有依序值的集合进行高速率写入

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

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

大型数组或映射字段

大型数组或映射字段可能会达到每个文档 40000 个索引条目的限制。如果您的查询不是基于大型数组或映射字段,建议您不要将该数组或字段编入索引。

读写操作

  • 避免写入文档的速率超过每秒一次。如需了解详情,请参阅单个文档的更新

  • 如果可以,请使用异步调用,而非同步调用。 异步调用可将延迟时间的影响降到最低。例如,假设某个应用需要文档 lookup 的结果以及某个查询的结果才能给出响应,而该 lookup 和该查询没有数据依赖关系,则启动查询前无需同步等待该 lookup 完成。

  • 请勿使用偏移。应改为使用游标。使用偏移仅会避免将跳过的文档返回到应用,但仍会在内部检索这些文档。跳过的文档会影响查询的延迟时间,并且由于检索文档需要执行读取操作,您的应用会因此而产生费用。

事务重试机制

Firestore SDK 和客户端库会自动重试失败的事务来处理暂时性错误。如果您的应用直接通过 RESTRPC API(而非通过 SDK)访问 Firestore,则您的应用应实施事务重试机制,以便提高可靠性。

实时更新

为了获得最佳的快照侦听器性能,请尽可能缩减文档大小并控制客户端的读取速率。以下建议提供了有关最大限度地提升性能的指南。超出这些建议值可能会导致通知延迟时间增加。

建议 详细信息
降低快照侦听器的变动率

避免频繁变动侦听器,尤其是在数据库承载大量写入负载的情况下

理想情况下,应用应在打开与 Firestore 的连接后立即设置所有必需的快照侦听器。设置初始快照侦听器后,应避免在同一连接中快速添加或移除快照侦听器。

为确保数据一致性,Firestore 需要根据其源数据准备好添加每个新的快照侦听器,然后实现与新更改的同步。这可能是一项成本较高的操作,具体取决于数据库的写入速率。

如果您频繁地在引用中添加或移除快照侦听器,则快照侦听器的延迟时间可能会增加。在数据量相同的情况下,持续附加到某一位置的侦听器通常会比在该位置频繁附加和分离的侦听器性能更高。为实现最佳性能,快照侦听器的生命周期应至少为 30 秒。如果您在应用中遇到侦听器性能问题,请尝试跟踪应用的侦听和取消侦听次数,以确定这些操作是否过于频繁。

限制每个客户端的快照侦听器数量

100

确保每个客户端的快照侦听器数量低于 100 个。

限制集合写入速率

1000 次操作/秒

确保单个集合的写入操作速率低于 1000 次/秒。

限制单个客户端推送速率

1 个文档/秒

确保数据库向单个客户端推送文档的速率保持在 1 个文档/秒以下。

限制全局客户端推送速率

100 万个文档/秒

确保数据库向所有客户端推送文档的速率低于 100 万个文档/秒。

限制单个文档负载

10 KiB/秒

确保单个客户端每秒下载的最大文档大小不超过 10 KiB。

限制全局文档负载

1 GiB/秒

确保所有客户端每秒下载的最大文档大小不超过 1 GiB。

限制每个文档的字段数

100

您的文档包含的字段应少于 100 个。

了解 Firestore 标准限制

请记住 Firestore 的标准限制,特别是文档的每秒写入次数限制(1 次)以及每个数据库的并发连接数量限制(100 万个)。

可扩缩式设计

以下最佳做法介绍了如何避免产生争用问题的情况。

单个文档的更新

避免更新单个文档的速率超过每秒一次。如果文档更新速率过快,则您的应用会出现争用问题,包括更长的延迟时间、超时和其他错误。

窄文档范围的高读取、写入和删除速率

避免字典顺序上相近的文档具有较高的读取或写入速率,否则您的应用将会发生争用错误。此问题称为热点问题。如果您的应用执行以下任何操作,它就可能会出现热点问题:

  • 以极高速率创建新文档,并且分配自己的单调递增 ID。

    Firestore 使用分散算法分配文档 ID。如果使用自动文档 ID 创建新文档,则不会在写入时遇到热点问题。

  • 在包含极少文档的集合中以高速率创建新文档。

  • 以极高速率创建包含单调递增的字段(如时间戳)的新文档。

  • 以高速率删除集合中的文档。

  • 以极高速率对数据库执行写入操作,而不是逐渐增加流量。

循序渐进地增加流量

您应该循序渐进地增加新的集合或字典顺序上相近的文档的流量,以确保 Firestore 在流量增加时有足够的时间准备文档。对于一个新的集合,我们建议最初每秒最多执行 500 次操作,然后每 5 分钟增加 50% 的流量。例如,使用这种渐增模式,90 分钟后将读取流量增加到每秒 74 万次操作。同样地,您可以循序渐进地增加写入流量,但请牢记 Firestore 标准限制。请确保操作在整个键范围内分布相对均匀。这就是所谓的“500/50/5”规则。

将流量迁移到新集合

如果您在集合之间迁移应用流量,则渐增模式尤为重要。处理此迁移的一种简单方法是从旧集合中读取;如果文档不存在,则从新集合中读取。但是,这可能会导致新集合中字典顺序上相近的文档的流量突然增加。在流量增加时,Firestore 可能无法高效地准备新集合,尤其是在新集合中包含很少文档的情况下。

如果您更改同一集合中的许多文档的文档 ID,就可能会出现类似的问题。

将流量迁移到新集合的最佳策略取决于数据模型。以下是一个名为“并行读取”的示例策略。您需要确定此策略对您的数据是否有效,并且一个重要的考虑因素是迁移过程中并行操作的费用影响。

并行读取

要在将流量迁移到新集合时实现并行读取,请首先从旧集合中读取。如果文档不存在,则可以从新集合中读取。以高速率读取不存在的文档可能会导致热点问题,因此请务必循序渐进地增加新集合的负载。一种较佳的策略是将旧文档复制到新集合,然后删除旧文档。循序渐进地增加并行读取操作,以确保 Firestore 可以处理新集合的流量。

循序渐进地增加对新集合的读取或写入操作的一种可行策略是,使用用户 ID 的确定性哈希来选择尝试写入新文档的用户的随机百分比。请确保用户 ID 哈希的结果不会因函数或用户行为而有所偏差。

同时,运行一个批量作业,将所有数据从旧文档复制到新集合中。为防止出现热点问题,您的批量作业应避免对顺序文档 ID 执行写入操作。批量作业完成后,您只能从新集合中读取。

可以将此策略改进为一次迁移一小批用户。 向用户文档添加一个字段,用于跟踪该用户的迁移状态。 您可以根据用户 ID 的哈希选择一批用户进行迁移。使用批量作业为该批用户迁移文档,并在迁移过程中为用户使用并行读取。

请注意,除非在迁移阶段对旧实体和新实体进行双重写入,否则无法轻松回滚。这会增加 Firestore 产生的费用。