【Rust】时间轮的数据结构于设计模式

概念来源

  • 源自"时间轮/定时轮"(Timing Wheel) 的数据结构,最经典的是 Hashed/Hierarchical Timing Wheels(1987 年提出),此后被 Linux 内核、Netty、Kafka 等广泛采用,用于高性能定时器管理。

工作原理

  • 将时间离散化为固定步长的"槽"(slot),以环形数组表示当前层的时间跨度。每次 tick,指针前进一个槽,只处理当前槽中的定时器。
  • 对超出当前层跨度的定时器,放到更高层(更大步长)的槽;随着指针循环,定时器逐级"下沉"到更精细的一层,直到到期执行。
  • 你仓库里的实现就是多层时间轮:L1~L5 层分别对应 100ms/1s/1min/1h/1d 步长,见 crates/focus-rs/src/slot/timer.rs:166 和 advance_time_wheel 逻辑 crates/focus-rs/src/slot/timer.rs:520。每次只清"当前槽位"的定时器,见 take_current_slot_timers crates/focus-rs/src/slot/timer.rs:199。短时(<1s)用一个小堆/有序结构处理,见 process_short_timers crates/focus-rs/src/slot/timer.rs:466。

为什么用"槽"

  • 插入/删除近似 O(1):定时器加入对应槽位,tick 时只处理当前槽,避免对全量定时器做 O(n) 扫描或 O(log n) 堆操作。
  • 批量处理友好:同一时间段到期的定时器集中在同一槽,tick 触发时成批拉取执行,降低锁竞争和调度开销。
  • 可扩展性强:层级化后,支持从毫秒到天的宽时间范围,且每个 tick 的工作量与当前槽内的计数成正比,而不是与定时器总数成正比。
  • 抖动可控:粒度由槽步长决定,可在"精度 vs 性能/内存"之间调参;你们将 <1s 交给短时队列,>=1s 用时间轮,兼顾精度与效率。
  • 简化并发:槽是天然的分桶,减少全局结构上的热锁;你们代码中还将短时/长时映射分离,进一步降低争用。

相对其他方案的优势/权衡

  • 相比最小堆/优先队列:插入 O(1) vs O(log n),每 tick 无需堆顶比较/调整;但时间轮存在"步长"精度限制,需要权衡 tick 和内存占用。
  • 相比纯有序表:不需要每 tick 全表扫描;但取消/重置时要注意"从槽中即时移除"(我们已在 cancel/reset 中补上移除,避免槽内残留导致同 ID 冲突)。

与你们实现的映射

  • 槽结构与操作:
    • 层与槽:crates/focus-rs/src/slot/timer.rs:166(TimeWheelLevel)
    • 取当前槽定时器:crates/focus-rs/src/slot/timer.rs:199
    • 推进并级联:crates/focus-rs/src/slot/timer.rs:520
  • 短时定时器(<1s):
    • 处理:crates/focus-rs/src/slot/timer.rs:466
  • 我们新增的即时清理(避免槽内残留):
    • 从槽位移除:crates/focus-rs/src/slot/timer.rs:221
    • 取消清槽:crates/focus-rs/src/slot/timer.rs:867
    • 重置清槽:crates/focus-rs/src/slot/timer.rs:932

  • 层级时间轮示意(含"槽"/slot)

L4: 天(1天/槽, 30槽)

指针→ \] \[ 0 \]\[ 1 \]\[ 2 \] ... \[ 29

↑ T4=3天 放在 L4 +3 槽

L3: 小时(1小时/槽, 24槽)

指针→ \] \[ 0 \]\[ 1 \]\[ 2 \] ... \[ 23

↑ T3=5小时 放在 L3 +5 槽

L2: 分钟(1分钟/槽, 60槽)

指针→ \] \[ 0 \]\[ 1 \]\[ 2 \] ... \[ 59

↑ T2=3分钟 放在 L2 +3 槽

L1: 秒(1秒/槽, 60槽)

指针→ \] \[ 0 \]\[ 1 \]\[ 2 \] ... \[ 59

↑ T1=7秒 放在 L1 +7 槽

L0: 毫秒(100ms/槽, 10槽)

指针→ \] \[ 0 \]\[ 1 \] ... \[ 9

(<1s 的短时定时器不入轮,走短时队列)

  • 运行流程(简化)

    • 新定时器加入时,根据剩余时间选择层级和槽位:
      • <1s → 短时队列(按到期时间排序,独立处理)
      • ≤60s → L1(秒级槽)
      • ≤60min → L2(分钟级槽)
      • ≤24h → L3(小时级槽)
      • ≤30d → L4(天级槽)
    • 每个 tick:
      • 处理短时队列中到期项
      • 低层(L0→L4)按顺序推进当前槽位指针,仅取"当前槽"的定时器触发
      • 若某层转了一圈(指针回到0),就把上层(更大粒度)的定时器根据精确剩余时间"下沉"到本层合适的槽(级联/下沉),等待后续更精细的触发
  • 示例(与你日志一致)

    • 6.94s → L1 +7 槽;70s → L2 +2 槽(先挂分钟级,等 L1 多圈后再下沉)
    • 339ms → 短时队列(不入轮),到时直接触发
    • 触发闭环:触发 → 激活下一段 → 立刻按剩余时长重新入轮(或入短时队列)
  • 直观好处

    • 每次 tick 只处理"当前槽",近似 O(1),不用全量扫描或堆调整
    • 到期集中批处理,锁竞争小,可扩展到大量定时器
    • 多层粒度兼顾"精度与性能":短时精确,长时高效
相关推荐
盒马盒马2 小时前
Rust:Trait 抽象接口 & 特征约束
开发语言·rust
I still …2 小时前
Rust内存问题检测
rust·程序分析·漏洞检测
百锦再5 小时前
大型省级政务平台采用金仓数据库(KingbaseES)
开发语言·数据库·后端·rust·eclipse
受之以蒙7 小时前
赋能 AI 与具身智能:Rust ndarray 构建安全高效的数据底座
人工智能·笔记·rust
Pomelo_刘金7 小时前
Rust : 新版本 1.90.0
rust
songroom10 小时前
Rust: 量化策略回测与简易线程池构建、子线程执行观测
开发语言·后端·rust
资深web全栈开发10 小时前
二分搜索中 `right = mid` 而非 `right = mid + 1` 的解释
算法·rust·二分搜索
Pomelo_刘金10 小时前
Rust : 新版本 1.89.0
rust
Pomelo_刘金10 小时前
Rust : Trusted Publishing(受信发布)
rust