可变时间窗口下global_avg(平均的平均值)高效计算方法咨询
上下文
- Activity对应一个分数
- Activity归属于科目,subject_avg为指定时间范围内该科目所有Activity分数的平均值
- global_avg为多个subject_avg的平均值(请勿与所有Activity分数的整体平均值混淆)
现有能力
单科目subject_avg已实现高效计算:通过预先累计该科目每日的avg_sum(总分数)和activity_count(Activity总数),查询任意日期区间的subject_avg仅需简单算术运算即可获取,示例如下:subject_avg (第2天到第5天) = (2.3 - 0.5) / (3 - 1) = 0.6
待解决问题
当前计算global_avg的逻辑为:先逐个计算指定时间窗口内所有科目的subject_avg,再对所有subject_avg求平均值得到结果,示例如下:global_avg (第2天到第5天) = (subjectA_avg + subjectB_avg)/2 = (0.6 + 0.8) / 2 = 0.7
现有科目数量达上百个,是否存在subject_avgs预处理方案,使得查询指定时间窗口的global_avg时,无需逐个计算各科目对应时间窗口的subject_avg即可快速得到结果?
解决方案
有两种成熟的预处理方案可以匹配不同的查询场景:
- 固定窗口查询场景
如果业务中查询的时间窗口都是固定长度(比如固定查最近7天、最近30天、自然周/自然月),可以直接预计算全量固定窗口的global_avg结果存储:
- 按所需窗口粒度提前跑批计算每个窗口对应的global_avg,存入带时间索引的存储介质
- 查询时直接按窗口起止时间匹配预计算结果,O(1)时间即可返回,完全不需要实时计算单科目subject_avg
该方案实现简单、查询性能最高,仅适用于窗口固定的场景。
- 任意窗口查询场景
如果需要支持任意起止时间的查询,从数学逻辑上看global_avg = sum(各科目窗口内subject_avg) / 科目总数的公式无法拆解为两个独立前缀和的差值,没办法做到纯O(1)无计算查询,但可以通过分段预聚合大幅降低实时计算量:
- 按周/月为粒度拆分时间分段,预计算每个分段内所有科目的subject_avg,以及所有科目subject_avg的总和
- 查询时先匹配窗口覆盖的完整分段,直接取预计算的分段sum值,仅对窗口首尾不足一个分段的零散天数,实时计算对应科目的subject_avg
- 最后把分段sum和零散天的sum加总除以科目总数,即可得到global_avg
如果按周做分段,跨3个月的查询也仅需要实时计算最多2周的零散天数据,计算量比全量计算上百个科目少80%以上。
额外补充:如果你的科目数量仅为上百个,就算全量实时计算每个科目的subject_avg,整体运算量也仅为几百次算术运算,绝大多数场景下耗时都在微秒级,实际体验和预计算方案不会有明显差异,不需要过度设计预处理逻辑。
内容的提问来源于stack exchange,提问作者carla
相关产品推荐
相关产品推荐

