你了解时间轮(Time Wheel)吗?有哪些应用场景?

Sherwin.Wei Lv7

你了解时间轮(Time Wheel)吗?有哪些应用场景?

回答重点

时间轮(Time Wheel) 是一种用于管理和调度大量定时任务的数据结构。它是一种高效的定时任务调度算法,主要用于优化任务调度的效率,特别是在需要处理大量定时任务时

时间轮是一种环形的数据结构,通过将时间划分为若干个时间片(槽),每个时间片负责管理一定时间段(如秒、分钟等内的任务。

工作原理

  • 时间轮的中心是一个环形结构,每个槽表示一个时间段。当时间轮的指针移动到某个槽时,该槽中的任务会被执行。
  • 任务被插入到特定的槽中,根据任务的延迟时间确定插入的位置。
  • 时间轮以固定的时间步长(如秒)推进,每次推进一个时间单位,执行相应槽中的任务。

应用场景

  • 高效的定时任务调度:在需要处理大量定时任务的场景,如高并发的定时任务系统,时间轮可以有效地减少任务调度的开销。
  • 网络服务器:在网络服务器中,时间轮常用于实现定时操作,如连接超时、请求超时等。
  • 分布式系统:在分布式系统中,时间轮可以用于协调不同节点的定时任务,优化任务调度和超时处理。

实际应用示例

  • Netty:Netty 是一个高性能的网络框架,它使用时间轮来管理定时任务,如超时处理和定时操作。
  • Guava:Google 的 Guava 库中也有时间轮的实现,用于优化定时任务调度的性能。
  • Caffine Cache:这个高性能本地缓存库中也有时间轮的实现,即 TimerWheel。

扩展知识

时间轮的优点

  • 高效性:时间轮减少了任务调度的时间复杂度,使得管理大量定时任务变得高效。通过将任务分配到槽中,时间轮避免了使用链表等数据结构的高时间复杂度操作。
  • 内存占用:时间轮通过固定大小的槽来管理任务,相比于链表,它的内存占用更加稳定和可控。

时间轮原理解析

时间轮就是和手表时钟很相似的存在,它是用环形数组实现,数组的每个元素可以称为槽,和 HashMap一样称呼。

槽的内部用双向链表存着待执行的任务,添加和删除的链表操作时间复杂度都是 O(1),槽位本身也指代时间精度,比如一秒扫一个槽,那么这个时间轮的最高精度就是 1 秒。

也就是说延迟 1.2 秒的任务和 1.5 秒的任务会被加入到同一个槽中,然后在 1 秒的时候遍历这个槽中的链表执行任务。

从图中可以看到此时指针指向的是第一个槽,一共有八个槽0~7,假设槽的时间单位为 1 秒,现在要加入一个延时 5 秒的任务,计算方式就是 5 % 8 + 1 = 6,即放在槽位为 6,下标为 5 的那个槽中。更具体的就是拼到槽的双向链表的尾部。

然后每秒指针顺时针移动一格,这样就扫到了下一格,遍历这格中的双向链表执行任务。然后再循环继续。

可以看到插入任务从计算槽位到插入链表,时间复杂度都是O(1)。那假设现在要加入一个50秒后执行的任务怎么办?这槽好像不够啊?难道要加槽嘛?和HashMap一样扩容?

不是的,常见有两种方式,一种是通过增加轮次的概念。50 % 8 + 1 = 3,即应该放在槽位是 3,下标是 2 的位置。然后 (50 - 1) / 8 = 6,即轮数记为 6。也就是说当循环 6 轮之后扫到下标的 2 的这个槽位会触发这个任务。Netty 中的 HashedWheelTimer 使用的就是这种方式。

还有一种是通过多层次的时间轮,这个和我们的手表就更像了,像我们秒针走一圈,分针走一格,分针走一圈,时针走一格。

多层次时间轮就是这样实现的。假设上图就是第一层,那么第一层走了一圈,第二层就走一格。

可以得知第二层的一格就是8秒,假设第二层也是 8 个槽,那么第二层走一圈,第三层走一格,可以得知第三层一格就是 64 秒。

那么一格三层,每层8个槽,一共 24 个槽时间轮就可以处理最多延迟 512 秒的任务。

而多层次时间轮还会有降级的操作,假设一个任务延迟 500 秒执行,那么刚开始加进来肯定是放在第三层的,当时间过了 436 秒后,此时还需要 64 秒就会触发任务的执行,而此时相对而言它就是个延迟 64 秒后的任务,因此它会被降低放在第二层中,第一层还放不下它。

再过个 56 秒,相对而言它就是个延迟 8 秒后执行的任务,因此它会再被降级放在第一层中,等待执行。

降级是为了保证时间精度一致性。 Kafka 内部用的就是多层次的时间轮算法。

Comments