2.2 Firestore 心得:数据建模决定一切
Firestore 是四大件里最值得花时间学的,也是最容易学歪的。我的核心心得浓缩成一句话:Firestore 是按查询建模,不是按实体建模。 这一节全在讲这句话怎么落地。
SQL 思维迁移的阵痛
从 MySQL、PostgreSQL 转过来的开发者,几乎都要经历同一场阵痛。SQL 的世界里,你先画 ER 图、把实体拆成规范的表,查询时用 JOIN 现场组装。Firestore 没有JOIN——它鼓励你反过来想:先列出产品要跑的所有查询,再倒推数据该怎么摆。 同一份数据可能为了不同查询存两三份,这在 SQL 眼里是异端,在 Firestore 里是常态。
举个我踩过的例子:博客系统里“文章列表页要显示作者昵称”。SQL 思维会建 users 表然后 JOIN。Firestore 的做法是在文章文档里直接冗余一个 authorName 字段。作者改名怎么办?更新他名下所有文章的冗余字段——写一次,换之后每次列表读取都快。这就是“冗余换速度”的常态,写多读少的数据别冗余,读多写少的数据放心冗余。
两套思路的差异,这张表说得最直白:
| SQL 里的习惯 | Firestore 里的对应做法 |
|---|---|
| JOIN 现场组装数据 | 写入时冗余常用字段 |
| 第三范式,消灭重复 | 读多写少就允许重复 |
| 全表 COUNT 统计 | 维护一个计数器文档或交给云函数 |
| LIKE 模糊搜索 | 全文搜索交给专门方案,Firestore 不擅长 |
| 复杂事务跨多表 | 事务仍可用,但尽量让一次操作落在一个文档上 |
嵌套还是子集合
文档-集合模型里,数据可以嵌在文档内部,也可以拆成子集合。我的判断标准很简单:
- 数据生命周期完全跟随父文档、总量可控:用嵌套(比如订单里的收货地址快照)。
- 会无限增长、需要单独查询:必须用子集合(比如文章的评论)。
嵌套数组是重灾区。数组里的元素一旦要按条件查询、单独更新、会持续增长,就别放数组里,拆子集合。数组适合放“标签列表”这种小而稳定的字段。
| 场景 | 推荐结构 | 理由 |
|---|---|---|
| 订单的收货地址 | 文档内嵌套对象 | 随订单生灭,不会单独查 |
| 文章的评论 | 子集合 | 无限增长,需分页查询 |
| 文章的标签 | 文档内数组字段 | 少量、稳定、整体读写 |
| 用户的关注列表 | 独立集合 + 双向记录 | 要反查粉丝,得存两份 |
避免深度嵌套与文档过大
两个硬约束要刻在脑子里:文档有大小上限,单文档写入频率也有限制(具体数值以官方文档为准)。所以别把一个集合当成一篇文章越写越长——聊天室消息、计数器这类高频小写入,一定要拆出去。深度嵌套也一样,层级超过两三层就该想想是不是结构歪了,嵌套越深,后面每一步查询和安全规则写得越痛苦。
监听器:一次监听就是持续计费的读
Firestore 按读写次数和存储计费,其中最容易被忽视的是快照监听。实时监听很爽,但监听器一旦挂着,文档每次变化都是一次读,页面里挂几十个监听器、监听大集合的前几百条,账单会诚实地反映出来。我的心得:
- 监听范围尽量小,用查询把监听收窄到真正要实时刷新的文档;
- 组件销毁时记得取消订阅,别让监听器在后台裸奔;
- 不需要实时的页面(比如设置页),用一次性读取就够。
一条纪律:上线前在控制台用量页看一眼读写曲线,异常陡峭的曲线基本都指向失控的监听器。
索引报错不慌
Firestore 需要为复合查询建索引,第一次跑组合排序的查询,控制台会抛出索引错误——新手容易慌,其实体验很友好:报错信息里直接给了链接,点开一键创建,等几分钟建好就好。 我的方法是开发期遇到就顺手建,项目稳定后清理一次没用到的索引,避免存储浪费。
本节小结
Firestore 建模的口诀:先列查询,再摆数据;读多写少敢冗余,无限增长拆子集合;监听器要省着用,索引报错点链接。结构摆对了,后面规则和函数都顺;结构歪了,处处别扭。数据建模决定一切,不是修辞。
