Ionic+Firestore项目如何获取不重复随机文档并兼顾查询效率与成本
方案合理性评估
你提出的跨集合迁移方案逻辑上可以跑通,但存在几个未预估到的隐患:
- 成本实际涨幅高于预期:Firestore删除属于写操作,计入写操作计费配额,你每次抽取5个商品会产生5次删除+5次新增,合计10次写操作,比原本仅修改字段的操作多了5次/轮,实际写成本直接翻倍,额外还会产生删除操作的对应费用,和你的预期不符
- 故障容错性差:如果抽取过程中服务中断,会出现collectA和collectB都存在未抽取完商品的情况,没有配套校验逻辑的话很容易出现重复抽取、漏抽的问题
- 并发支持差:如果有多端同时触发抽取逻辑,很容易出现并发删改导致的数据不一致问题
- 查询性能有隐性损耗:跨集合迁移需要操作两份数据,极端情况下会出现冷集合查询延迟上升的问题
更优实现方案
推荐使用「随机种子+游标」方案,完全满足不重复抽取、循环、低延迟、低成本的要求,实现逻辑如下:
- 基础字段改造
给collectA集合的所有商品文档新增两个字段:
random_seed:浮点数类型,存储0-1之间的随机值round_picked:布尔类型,标记当前轮次是否已被抽取,默认值为false
- 抽取逻辑
- 首轮抽取前先给全量商品的
random_seed赋值随机值,所有商品的round_picked设为false - 每次抽取时执行查询:筛选
round_picked = false的文档,按random_seed升序排序,取前5条 - 拿到结果后,将这5条文档的
round_picked设为true,同时修改你需要更新的其他业务字段即可 - 当查询返回的结果不足5条时,说明当前轮次所有商品已抽取完毕,给全量商品重新生成新的
random_seed,将所有round_picked重置为false,即可开启下一轮循环
- 方案优势
- 成本极低:没有任何删除、跨集合新增操作,仅每轮循环需要全量更新一次
random_seed和round_picked字段,平时每次抽取仅需修改5条文档的字段,写操作量和你原本的业务修改需求完全一致,没有额外成本 - 查询速度快:仅单集合查询,可提前给
round_picked和random_seed建复合索引,查询延迟稳定在毫秒级 - 容错性强:就算抽取过程中断,重启后直接按查询条件取未标记的商品即可,不会出现数据混乱
- 支持商品动态新增:新增商品时直接给它生成随机
random_seed、round_picked设为当前轮次的状态即可,不会打断当前轮次的抽取流程
如果你的商品量级超过100万条,每轮全量更新字段的成本较高,可以改用固定分片随机方案:提前给random_seed设置固定值,将0-1区间拆分为20个固定分片,每轮随机选择起始分片,按顺序遍历分片抽取商品,遍历完所有分片后重置抽取标记即可,无需每轮更新全量random_seed,进一步降低成本。
内容的提问来源于stack exchange,提问作者sirss
相关产品推荐
相关产品推荐

