Firestore搭建可扩展低成本好友系统的数据模型设计咨询
Firestore 社交场景好友系统最优建模方案
核心数据结构设计
直接废弃原有单文档存储全量好友uid数组的Friends集合,采用子集合拆分的建模思路,从结构层面规避读浪费、写入放大、查询能力不足的问题:
- 保留原有
Users根集合:每个文档ID对应认证系统生成的用户uid,作为用户资料的唯一权威存储源,存姓名、用户名、头像、个人简介等全量基础字段,不做多副本冗余。 - 在每个用户文档下挂载
user_friends子集合,子集合内每个文档ID对应该用户的一个好友uid,文档仅存储和好友关系强绑定的轻量字段,单文档体积控制在100字节以内,结构示例:
Users/{current_uid}/user_friends/{friend_uid} - friend_uid: string - added_at: timestamp - friend_username: string // 仅冗余用于搜索的用户名字段,低更新频率 - last_msg_preview: map - content: string - sent_at: timestamp - sender_uid: string - unread_count: number
- 保留独立
Chats根集合存储全量聊天消息,好友关系文档内仅同步最新一条消息的预览字段,不存储全量聊天记录。
方案对原有缺陷的针对性解决
- 解决原方案1/2的查询能力差、无效读过高问题
好友关系拆分为独立子集合文档后,所有查询直接走Firestore原生能力:拉取好友列表时直接分页查询当前用户的user_friends子集合即可,支持按加好友时间、最新消息时间任意排序,单条文档仅存必要字段,没有任何冗余字段读取带来的计费浪费。
针对好友搜索需求,直接基于冗余的friend_username字段做前缀匹配查询,不需要全量拉取好友数据到前端二次过滤,支持分页,好友量到十万级也不会有性能问题。
权限控制可以直接通过Security Rules实现,用户仅能访问自己user_friends路径下的文档,从规则层面杜绝越权拉取其他用户好友列表的敏感数据泄露风险。 - 解决原方案3的写入放大问题
高频更新的用户资料(头像、昵称)仅在Users集合存储单份,用户更新资料时只需要1次写入,不需要同步更新所有好友侧的冗余字段。前端渲染列表时,按分页大小(通常20-30条/页)拿到当前页的好友uid列表后,用in操作符批量拉取对应用户的Users文档即可——Firestore单次in查询最多支持30个匹配值,刚好匹配分页大小,不存在N+1查询问题,且SDK内置的本地缓存会对重复访问的用户资料做持久化缓存,重复加载好友列表时命中缓存的文档不会产生额外读计费。
仅低频率更新的字段(比如用户名)、和当前用户强绑定的字段(未读计数、最新消息预览)才存在好友关系文档中,写入成本完全可控。
好友列表标准实现流程
- 第一步:分页查询当前用户的
user_friends子集合,按业务需求排序(比如按last_msg_preview.sent_at倒序,优先展示最近聊天的好友),单页拉取20-30条关系文档,提取好友uid列表。 - 第二步:用提取到的uid列表,批量查询
Users集合中ID匹配的文档,获取头像、姓名等展示字段,这一步的查询结果会自动进入SDK本地缓存,后续重复访问几乎无额外成本。 - 第三步:合并关系文档的最新消息、未读数和用户资料字段,直接渲染列表即可。
单页加载全流程最多产生60次文档读取,无任何无效数据拉取,支持任意分页、排序、筛选操作。
关联场景适配
- 即时通讯联动:新消息写入
Chats集合时,通过轻量的Cloud Function写入触发器,仅更新聊天双方user_friends子集合下对应文档的last_msg_preview和unread_count字段,单次新消息仅产生2次额外写入,成本可忽略。 - 好友双向关系校验:加好友时通过事务同时在双方的
user_friends子集合下写入对应文档,校验好友关系时只需要检查当前用户user_friends下是否存在目标uid文档即可,单次校验仅1次读。 - 用户名搜索:基于
friend_username字段用集合组查询做前缀匹配,不需要引入第三方搜索服务即可满足基础搜索需求;如果需要更复杂的昵称模糊搜索,再按需接入对应扩展服务即可,基础结构不需要调整。
成本参考
按单用户1000个好友的规模测算:
- 单次加载首页20条好友列表,仅产生40次文档读取(20条关系文档+20条用户资料),按Firestore官方定价,1万次列表刷新仅产生约0.16元人民币的读成本。
- 用户修改头像、昵称等高频操作仅产生1次写入,相比全量冗余方案需要写入1001份副本的逻辑,写入成本降低99.9%。
内容的提问来源于stack exchange,提问作者Alex Stroescu
相关产品推荐
相关产品推荐

