时间轮算法

348 阅读2分钟

在学习xxl-job的过程中,发现定时任务存储也是用的时间轮算法!

时间轮算法应用非常广,比如Netty和xxl-job相关定时任务都使用了时间轮算法,不过在存储的数据结构上略有不同(前者是数组,后者是Map)

Netty 中的时间轮算法是基于时间轮数据结构实现的定时任务调度机制,用于处理各种定时任务,如连接超时、读写超时等。它将时间划分为多个时间格,每个时间格代表一个固定的时间间隔,整个时间轮是一个环形结构,随着时间的推移,时间轮不断转动,执行到期的定时任务。

在设计“定时任务算法”的场景下,以下对比几种方案设计,比较出时间轮算法在一定场景下的优势(只有最适合的使用场景,没有最好的数据结构)

无序列表

image-20250223220708142

检测O(n):需要循环遍历

新增O(1):只需要在队尾加上任务即可

删除O(1):删除前后连接,再删除该节点

优点是足够简单,缺点是检测时间较长

有序链表

image-20250223221349631

检测O(1):只要看对尾节点就行

新增O(n):需要遍历,找到任务位置,也可以用小根堆优化一下

删除O(1):删除队尾节点

时间轮算法

简而言之,环状数组,以下是示意图

image-20250223221836240

遍历到当前扇形执行其中的任务

计算公式:((新任务执行时间-当前时间)/单位时间 + currentIndex)% cycle

现在出现了两个问题:

1.如果遇到了超过一圈时间的任务怎么办?

2.如果时间跨度是一天,我设置了最小粒度是1s,这个数组将会非常庞大

分层时间轮

分层时间轮有很多种实现方式,下面是按照时分秒的设计

现在有一个03:04:58时间的任务要执行,下面是示意图

image-20250223223201302

存储任务的时候先遍历小时,再遍历分钟,最后是秒数组,存储,查找也一样。

检测O(1),新增O(1),删除O(1)

仅通过24+60+60长度的数据就实现了存储(算法的魅力)