多时间区间事件计数Leaderboard构建咨询:Redis方案是否最优?
多时间窗口事件排行榜最优实现方案
核心思路:时间分片Sorted Set + 按需合并查询
针对不同时间窗口,采用匹配窗口粒度的分片策略维护Sorted Set,既避免单键数据过大,又减少键的数量,同时兼顾统计效率和查询性能。
1. 分片规则设计
每个时间窗口对应一组分片Sorted Set,分片粒度与窗口大小匹配,避免不必要的细粒度键:
- 1min窗口:按1分钟分片,键名格式
leaderboard:1min:yyyyMMddHHmm(如leaderboard:1min:202405201435) - 5min窗口:按5分钟分片,键名格式
leaderboard:5min:yyyyMMddHHmm(取整到最近5分钟,如14:33对应14:30) - 15min/30min/60min窗口同理,分片粒度分别设为15/30/60分钟
2. 事件统计操作
当事件触发时,计算当前时间对应的各窗口分片键,执行ZINCRBY累加事件次数:
# 示例:当前时间2024-05-20 14:35:20,事件为eventA ZINCRBY leaderboard:1min:202405201435 1 eventA ZINCRBY leaderboard:5min:202405201430 1 eventA ZINCRBY leaderboard:15min:202405201430 1 eventA ZINCRBY leaderboard:30min:202405201430 1 eventA ZINCRBY leaderboard:60min:202405201400 1 eventA
3. Top10查询操作
查询某窗口的Top10时,先找出该窗口内所有未过期的分片键,用ZUNIONSTORE合并为临时键,再取Top10,最后删除临时键:
# 示例:查询60min窗口Top10,当前时间14:35,合并最近12个5min分片(覆盖60分钟范围) ZUNIONSTORE temp_leaderboard_60min 12 leaderboard:5min:202405201330 leaderboard:5min:202405201335 ... leaderboard:5min:202405201430 # 获取Top10及对应次数 ZREVRANGE temp_leaderboard_60min 0 9 WITHSCORES # 清理临时键 DEL temp_leaderboard_60min
4. 过期分片清理
给每个分片键设置EXPIRE过期时间,比如1min分片设为2分钟过期,5min分片设为10分钟过期,60min分片设为70分钟过期,让Redis自动清理过期数据,无需额外定时任务。
替代方案:单Sorted Set + 滑动窗口过滤(低并发场景适用)
如果并发量不高,可给每个时间窗口维护一个单Sorted Set,元素score为事件触发时间戳,同时用Hash记录事件累计次数:
- 事件触发时:
HINCRBY event_counts eventA 1 ZADD leaderboard:60min current_timestamp eventA - 查询Top10时:
# 过滤60分钟外的过期事件 ZREMRANGEBYSCORE leaderboard:60min -inf (current_timestamp - 3600) # 结合Hash的次数排序取Top10(需业务层二次排序,效率略低)
这种方案键数量少,但高并发下频繁过滤过期数据会阻塞Redis,仅适合小流量场景。
方案对比与选择
- 分片方案:适合高并发场景,查询性能稳定,键数量可控,清理逻辑简单,是当前需求的最优选择。
- 单键方案:适合低并发、快速实现的场景,缺点是高并发下性能瓶颈明显。
内容的提问来源于stack exchange,提问作者rtuna
相关产品推荐
相关产品推荐

