Firexport博客

Firestore Enterprise 与 Standard:新应用请从 Enterprise 开始

创建 Firestore 数据库时选择的版本(Standard 或 Enterprise)之后无法更改。Enterprise 版的 Native 模式已于 2026 年 4 月正式发布(GA),现在新应用从 Enterprise 开始更划算。前提是你自己负责索引和客户端访问。

要点

  • 选以后不容易卡住的那个。Standard 的限制只能靠迁移数据解决,Enterprise 的问题大多部署一个索引就能解决。原因
  • 代码不变,查询更多。实时监听器、离线和安全规则与 Standard 相同,还多了能做搜索、聚合和联接的 Pipeline 查询。多了什么
  • 有索引的读取更便宜。按 ID 读取 4 KiB 以内的文档,费用是 Standard 的六分之一。写入的索引越多越贵,删除则总是更贵。价格对比
  • 索引和访问要自己负责。没有索引的查询不会报错,而是读取整个集合,别人也可以故意这样做。要做的事

为什么默认选 Enterprise

1. 无法更换,所以选不容易卡住的那个

版本在创建数据库时确定,之后不会改变。要更换,就得把数据迁移到新数据库,重新创建索引、TTL 和备份设置,并发布新版应用,让它连接新数据库(迁移指南)。

所以应该从以后不容易卡住的一边开始。在 Standard 上卡住的是搜索、聚合、联接这类功能,只能靠迁移或改结构来绕过。在 Enterprise 上卡住的大多是缺少索引,代码和数据都不用动,部署索引就行。

2. 同样的代码,更多的查询

Enterprise 的 Native 模式使用与 Standard 相同的 Firebase SDK 和相同的查询(Core 操作)。where()、orderBy()、onSnapshot() 监听器、离线缓存和安全规则照常工作,也支持本地模拟器、Cloud Functions(第 2 代)触发器、与 Standard 相同的区域,以及 Spark 方案的免费用量。不同的是排序:排序值相同的文档之间不会按文档 ID 排序,所以如果用游标分页,请在 orderBy 中加上文档 ID。

在此之上还有 Pipeline 查询。在 Standard 上需要变通的事,变成一条查询。

想做的事StandardEnterprise
全文搜索同步到外部搜索服务(Algolia 等)文本搜索
查找半径内的地点建 geohash 字段做范围查询地理空间搜索
按部分字符串或正则匹配只能前缀匹配str_contains、regex_match
分组合计、不同值的个数逐组聚合,或维护计数器文档一个 aggregate 阶段
合并读取其他集合反规范化,或多次读取子查询联接
批量更新、删除符合条件的文档先读取再分批写入的脚本update、delete 阶段
防止重复值以该值为 ID 的文档加事务唯一索引

文本搜索、地理空间搜索和批量更新删除(DML)已于 2026 年 10 月正式发布。不过 Pipeline 是执行一次就结束的读取,没有实时监听和离线支持,安全规则在 Pipeline 中也只认得与常量比较的简单过滤条件。界面上的列表和实时更新用 Core 查询,搜索、聚合和报表用 Pipeline,复杂的 Pipeline 放在服务器上(Admin SDK、Cloud Functions)运行。

3. 有索引的读取更便宜

Standard 按文档数量计费,Enterprise 按读写的字节计费。以下是 us-central1 在 2026 年 11 月的价格。

StandardEnterprise
读取每百万个文档 US$0.30每 4 KiB 1 个单位,每百万单位 US$0.05
写入每百万个文档 US$0.90,索引更新免费每 1 KiB 1 个单位,每百万单位 US$0.26;每个索引条目另加 1 个单位
删除每百万个文档 US$0.10按写入单位计(文档 + 索引条目)
实时更新按读取计,每百万次 US$0.30每 4 KiB 1 个单位,每百万单位 US$0.30;4 KiB 以内的文档价格相同
存储每 GiB 每月约 US$0.15,所有字段的自动索引也计入每 GiB 每月约 US$0.23,只计入你建的索引
每日免费用量读取 5 万、写入 2 万、删除 2 万读取 5 万、写入 4 万、实时更新 5 万单位
  • 读取:按 ID 读取 4 KiB 以内的文档,Enterprise 只要六分之一,24 KiB 以内也相同或更便宜。查询还要加上读取索引的费用,结果只有 1 条时是三分之一,结果越多越接近六分之一。
  • 写入:新写入 1 KiB 的文档时,索引条目不超过 2 个则 Enterprise 更便宜(每百万次 US$0.78),3 个起就更贵(US$1.04)。修改有索引字段的更新,每个变化的条目要 2 个单位,所以更快变贵。
  • 删除:最小的文档也要 1 个单位(US$0.26),所以总比 Standard(US$0.10)贵。

读取远多于写入的应用,用 Enterprise 更便宜。写入和删除多的应用,请先用自己的文档大小和索引数量算一算。

代价:索引和访问要自己负责

Standard 只执行能用索引的查询。每个字段都会自动建索引,需要复合索引的查询会报错,并附上创建该索引的链接。所以查询费用只跟结果数量有关。

Enterprise 不会自动建索引,没有索引也会执行查询。代价是读取整个集合并按读取的字节计费;排序也在内存中进行,可能碰到每个查询的内存上限(128 MiB)。因为不报错,开发时只有几十个文档看不出来,数据多了才会暴露。

下面是按用户名查找 1 位用户的查询执行一百万次的费用。文档大小 2 KiB,第一行是 Google 的定价示例,第二行把同样的计算扩大到 10 万个文档。

集合Standard(自动索引)Enterprise(有索引)Enterprise(无索引)
100 个文档US$0.30US$0.10US$2.50
10 万个文档US$0.30US$0.10US$2,500

一个索引让同一条查询的费用相差 2.5 万倍(100,000 × 2 KiB ÷ 4 KiB = 每次查询 50,000 个单位)。

别人也能利用这个差距。只要客户端能列出某个集合,任何人都可以加上一个针对无索引字段的过滤条件,让 Firestore 扫描他能读取的全部范围。没有匹配的文档,所以 limit 也没用;安全规则也拦不住在查询上额外添加的过滤条件。在 Standard 上,这样的查询只会报错。

四件要做的事

  1. 写查询时,顺手写索引。对持续增长的集合,每种 where、orderBy 组合都在 firestore.indexes.json 中写好索引,再用 firebase deploy --only firestore:indexes 部署。Standard 以前自动建的单字段索引也要自己写。按文档 ID 读取不需要索引。下面是 where('authorId', '==', uid).orderBy('createdAt', 'desc') 对应的索引。
    {
      "indexes": [
        {
          "collectionGroup": "posts",
          "queryScope": "COLLECTION",
          "fields": [
            { "fieldPath": "authorId", "order": "ASCENDING" },
            { "fieldPath": "createdAt", "order": "DESCENDING" }
          ]
        }
      ]
    }
  2. 上线前用 Query Explain 检查。在 Firebase 控制台 Firestore 的数据(Data)标签页中,用查询编辑器(Query Editor)以相同条件执行查询,然后打开 Query Explain 标签页。如果执行树最底层是 TableScan,就是没用索引、读取了整个集合。
  3. 收紧客户端访问。强制启用 App Check,拒绝不是来自你应用的请求;用安全规则把列表读取限定在有索引字段的相等条件上,例如 resource.data.ownerId == request.auth.uid。这样即使有人触发扫描,也只会在该用户的文档范围内结束。
  4. 打开 Query Insights 和预算提醒。Query Insights 会显示常用查询及其性能,帮你找出需要索引的地方。如果用的是 Blaze 方案,也请设置 Cloud Billing 预算提醒。

从 Enterprise 开始

在 Firebase 控制台的 Firestore 中点击创建数据库(Create database),选择 Enterprise 和 Native 模式,再设定 ID 和位置。实时更新(Realtime Updates)请保持开启(默认值)。它之后无法更改,关闭后创建的数据库就不能使用监听器。之后有三点与 Standard 不同。

  1. 在代码中传入数据库 ID。Enterprise 数据库是你自己命名的数据库,(default) 只有 Standard 才有。SDK 默认连接 (default),除非另行指定。
    // Web
    const db = getFirestore(app, 'app-db');
    
    // Android (Kotlin)
    val db = FirebaseFirestore.getInstance("app-db")
    
    // iOS (Swift)
    let db = Firestore.firestore(database: "app-db")
    
    // Flutter
    final db = FirebaseFirestore.instanceFor(
        app: Firebase.app(), databaseId: 'app-db');
  2. 在 firebase.json 中写明数据库和版本。这样规则和索引会部署到该数据库,模拟器也会以 Enterprise 启动。请先在控制台创建数据库。如果写明的数据库不存在,firebase deploy 会代为创建,而 Firebase CLI 15.12 此时会创建成 Firebase SDK 无法使用的 MongoDB 兼容模式。
    {
      "firestore": {
        "database": "app-db",
        "edition": "enterprise",
        "rules": "firestore.rules",
        "indexes": "firestore.indexes.json"
      }
    }
  3. 免费用量只给第一个创建的数据库。如果项目里有不用的 Standard 数据库,请先删除它,再创建 Enterprise 数据库,这样免费用量才会归 Enterprise。

Standard 仍然更合适的情况

  • 已经在 Standard 上运行的应用。在真正需要 Pipeline 之前不要迁移。
  • 没有人负责查询和索引的项目。不执行无索引查询的 Standard 就是安全网。
  • 客户端直接读取大型公开集合的应用。无法用规则缩小范围的公开列表,任何人都能触发全集合扫描。
  • 写入和删除很多,或主要读取大文档的应用。超过上面的平衡点,Standard 更便宜。
  • 需要使用以 (default) 数据库或 Cloud Functions 第 1 代为前提的工具时。Enterprise 是命名数据库,也不支持第 1 代触发器。

资料来源

全部文章 →