Firebase最佳实践与成本管理:集合/Map/数组/子集合选型咨询
Firebase 购物清单应用数据结构设计方案
核心设计逻辑完全匹配你的两个页面的查询需求,同时把Firestore读取/写入成本压到最低:
推荐结构(成本最低、查询最顺)
Firestore按文档读取次数计费,核心原则是页面需要什么数据就只查什么数据,绝对不返回冗余字段,所以拆成两个独立的存储单元:
1. 根集合 shops(对应首页商铺列表页)
每个文档只存列表卡片需要展示的信息,绝对不要嵌套商品数据:
shops (集合) └── {shopId} (文档,建议用自动生成ID,也可以自定义对应1号/2号超市) ├── shopName: "1号超市" ├── pendingItemCount: 2 // 待采购商品数,列表页直接展示角标,不用额外查商品 ├── [其他列表页需要展示的字段,比如地址、更新时间,可选]
- 首页加载时直接拉取
shops集合全量文档即可,每个文档体积只有几十到上百字节,读取成本极低。
2. 根集合 shopItems(对应商铺详情页的商品清单)
每个商品对应一个独立文档,用shopId关联所属商铺:
shopItems (集合) └── {itemId} (文档,自动生成ID即可) ├── shopId: "xxx" // 关联所属商铺的ID,给这个字段建单字段索引 ├── itemName: "Sugar" ├── itemChecked: false // 用布尔值代替"Yes"/"No"字符串,省空间、查询判断更快 ├── createdAt: 时间戳 // 可选,用来给商品排序
- 用户点击商铺进入详情页时,直接查询
shopItems集合中shopId等于当前商铺ID的所有文档即可,不会拉取其他商铺的商品,没有冗余读取。 - 如果后续完全不需要做跨商铺的商品查询(比如全局搜所有没买的啤酒),也可以把商品放在商铺文档的子集合
shops/{shopId}/items下,查询逻辑、成本和上面的根集合方案完全一致,结构更内聚。
两类常见试错方案的共性问题
从场景判断,你之前尝试的方案大概率属于以下两种,都会大幅拉高使用成本:
- 把商品作为数组字段直接嵌套在
shops的商铺文档里- 打开首页拉取商铺列表时,会把所有商铺的所有商品全部拉取到本地,比如10家店每家20个商品,光打开首页就会产生200条商品的冗余读取,成本翻几十倍
- 修改单个商品的勾选状态时,需要重写整个商铺文档,写入成本高,还容易出现多端同时修改的并发冲突
- 把所有商铺、所有商品全部存在同一个顶层文档里
- 不管你看哪个页面,都要拉取全量所有数据,数据量上来之后读取成本极高,而且Firestore单文档有1MB大小上限,商品多了直接存不下
- 任意小修改都要重写整个文档,并发冲突概率极高
额外成本优化技巧
- 不要用字符串存
itemChecked状态,布尔值单字段能省2-3个字节,积少成多 - 商铺文档里冗余的
pendingItemCount字段用原子操作更新:每次勾选/取消勾选商品时,同步对这个计数做+1/-1的原子修改,首页展示待办数的时候不需要再遍历商品集合统计,能省大量统计类查询 - 前端给商铺列表、商品列表加本地持久化缓存,用户重复打开页面时优先读本地缓存,后台静默拉取最新数据,进一步减少云侧读取次数
- 列表查询时只拉取需要展示的字段,不用每次都获取文档全量内容
内容的提问来源于stack exchange,提问作者Laurent Thomas
相关产品推荐
相关产品推荐

