时间窗口设计

返回 高并发

排行榜、限流、监控、风控都依赖时间窗口:在什么时间范围内聚合、何时切榜、何时落库。设计错误会导致边界双倍计数榜错乱Redis key 爆炸


三种基础窗口

类型定义示例
滚动窗口(Tumbling)固定长度、首尾相接不重叠每天 0:00~24:00 日榜
滑动窗口(Sliding)任意时刻看「过去 N 分钟」最近 1 小时热搜
会话窗口(Session)间隔超时切分用户连续点击会话
日历日榜(滚动):
|---- 6/26 ----|---- 6/27 ----|---- 6/28 ----|

滑动 1h(在 14:30 看 = [13:30, 14:30]):
        |←── 60 min ──→|
              14:30

微信步数:日历日窗口

决策建议
切榜时刻用户本地时区 0 点(或产品统一东八区)
Keystep:20240627:{userId}
过期EXPIRE 2~7 天,自动清理
0 点瞬间新 key 命名空间切换,旧榜只读
# 新日 key,避免与昨日混写
SET step:20240627:10001 8520 EX 604800

不要用 TTL=86400 滑动代替自然日(会在首次写入时间滚动,榜语义错误)。


Redis 时间分桶

固定日历桶(推荐日榜)

rank:friend:{uid}:20240627   → ZSet
step:20240627:{uid}          → String

滑动窗口限流(按分钟)

rate:{uid}:{yyyyMMddHHmm}  INCR + EXPIRE 60

Redis Cell / 网关内置滑动窗口。

滑动窗口计数(热搜衰减)

多桶叠加近似滑动:

# 最近 5 个 1 分钟桶
hot:word:202406271430
hot:word:202406271431
...
score = sum(bucket[i] * weight[i])

更精确可用 Redis Stream 按时间 trim 或 Flink 事件时间窗口。


延迟窗口与结算

场景设计
日榜定稿0:05 停止接受昨日上报,或接受但记入今日
对账窗口T+1 凌晨批量校验 Redis vs DB
watermark流处理中「允许迟到 5 分钟」的事件仍计入昨日
23:58 上报 → 计入当日
00:02 上报昨日补传 → 产品规则:拒绝 / 记入当日 / 记入昨日(需明确)

流式 vs 批处理

路径技术适用
在线Redis + MQ 消费者实时榜、实时步数
近线Flink / Spark Streaming复杂窗口、多维度聚合
离线数仓按 dt 分区审计、报表

Flink 窗口类型:

  • TumblingEventTimeWindows
  • SlidingEventTimeWindows
  • EventTimeSessionWindows

事件时间需处理 乱序watermark)。


窗口与存储成本

风险缓解
key 按分钟爆炸限流桶只保留最近 N 个;定期 SCAN 清理
跨时区统一用 UTC 存储,展示层转换
夏令时日历日规则单独测试

相关