在学习xxl-job的过程中,发现定时任务存储也是用的时间轮算法!
时间轮算法应用非常广,比如Netty和xxl-job相关定时任务都使用了时间轮算法,不过在存储的数据结构上略有不同(前者是数组,后者是Map)
Netty 中的时间轮算法是基于时间轮数据结构实现的定时任务调度机制,用于处理各种定时任务,如连接超时、读写超时等。它将时间划分为多个时间格,每个时间格代表一个固定的时间间隔,整个时间轮是一个环形结构,随着时间的推移,时间轮不断转动,执行到期的定时任务。
在设计“定时任务算法”的场景下,以下对比几种方案设计,比较出时间轮算法在一定场景下的优势(只有最适合的使用场景,没有最好的数据结构)
无序列表
检测O(n):需要循环遍历
新增O(1):只需要在队尾加上任务即可
删除O(1):删除前后连接,再删除该节点
优点是足够简单,缺点是检测时间较长
有序链表
检测O(1):只要看对尾节点就行
新增O(n):需要遍历,找到任务位置,也可以用小根堆优化一下
删除O(1):删除队尾节点
时间轮算法
简而言之,环状数组,以下是示意图
遍历到当前扇形执行其中的任务
计算公式:((新任务执行时间-当前时间)/单位时间 + currentIndex)% cycle
现在出现了两个问题:
1.如果遇到了超过一圈时间的任务怎么办?
2.如果时间跨度是一天,我设置了最小粒度是1s,这个数组将会非常庞大
分层时间轮
分层时间轮有很多种实现方式,下面是按照时分秒的设计
现在有一个03:04:58时间的任务要执行,下面是示意图
存储任务的时候先遍历小时,再遍历分钟,最后是秒数组,存储,查找也一样。
检测O(1),新增O(1),删除O(1)
仅通过24+60+60长度的数据就实现了存储(算法的魅力)