如何在Firestore中高效聚合并计算平均值?
分布式计数器模式优化实时更新
别每次加新分数就全量计算,专门建个moduleStats集合,每个模块对应一个文档,存totalScore(模块总分)、count(答题数)、moduleId这几个字段。每次新增grades文档时,直接对对应模块的totalScore做增量加法,count加1——平均分要么实时用totalScore/count计算,要么顺手存成avgScore字段。这样每次新增只需要1次写入操作,比全量计算省太多。
- 要是有超高并发写入需求,就用分片计数器拆分写入压力,普通场景单文档完全够用。
定时批量聚合
如果不需要实时看到平均分,就搞个定时触发的Cloud Functions,每天或者每周跑一次。每次任务批量读取这段时间新增的grades文档,批量更新对应模块的统计数据。还能给grades文档加个isProcessed字段,标记已经统计过的文档,避免重复计算。这种方式读写量集中在定时任务,不会每次加新分数都触发,写入量直接大幅降低。
客户端增量缓存
要是必须要实时,但又不想频繁写数据库,就在客户端做缓存:第一次打开时,用分组查询(where("moduleId", "==", 目标模块ID))拉取该模块下所有分数,算出平均分并缓存总分和计数。之后只监听这个模块下的新增grades文档,每次有新分数进来,本地更新缓存的总分和计数,直接算出新的平均分。这样只有第一次是全量读,后续只读新增文档,比每次全量读成本低很多。
BigQuery离线分析
把Firestore的数据自动同步到BigQuery,然后在BigQuery里写SQL做聚合——直接按模块分组算AVG(score)就行。Firestore到BigQuery的同步可以自动配置,BigQuery的查询成本比Firestore高频读写低得多,而且不影响现有Firestore的写入逻辑。适合需要做历史数据分析、多维度统计的场景,实时性虽然差点,但成本极低。
内容的提问来源于stack exchange,提问作者Ruder Buster

