时间轮算法

article/2025/7/28 2:40:56

一、时间轮算法

1. 时间轮的基本概念

Kafka中存在大量的延时操作,如延时生产、延时消费等;而JDK中自带的 Timer 和 DelayQueue 的插入和删除操作的平均复杂度为 O(nlogn),无法满足 Kafka 的高性能要求,因此 Kafka 基于时间轮算法自定义实现了一个用于延时功能的定时器,从而到达 O(1) 的时间复杂度,本文就对时间轮算法进行介绍:

Kafka 的时间轮 (TimingWheel) 实际就是一个存储定时任务的环形队列,底层采用数组实现,数组的每一个元素可以存放一个定时任务队列(TimerTaskList)。TimerTaskList 是一个环形的双向链表,链表中的每一项为定时任务项(TimerTaskEntry),其中封装了真正的定时任务(TimerTask)
在这里插入图片描述

时间轮由固定个数(wheelSize)的时间格组成,每个时间格代表当前时间轮的基本时间跨度(tickMs),整个时间轮的总体时间跨度(interval)为 tickMs*wheelSize

时间轮还有一个表盘指针(currentTime),用来表示时间轮当前所处的时间,currentTime是 tickMs 的整数倍。 currentTime 可以将整个时间轮划分为到期部分和未到期部分,currentTime 当前指向的时间格刚好到期,需要处理此时间格对应的 TimerTaskList 中的所有任务。

2. 时间轮的轮转:
  • 假定时间轮的 tickMs = 1ms, wheelSize = 20
  • 初始情况下,currentTime指向0,若此时插入一个延时2ms的任务,则会被存进时间格为2的 TimerTaskList 中,该插入操作的时间复杂度为 O(1)
  • 过了2ms后,指针到达时间格2,就会将时间格2对应的 TimerTaskList 中的任务进行相应到期操作。
  • 此时,若又有一个定时为8ms的任务插入,则会被存入到时间格10中
  • 如果又同时又有一个延时19ms的任务插入,则会复用原来的 TimerTaskList,插入原本已经到期的时间格1
3. 层级时间轮

时间轮按照上述的方式运转,但是可以看到该时间轮的时间范围(interval)是有限的(tickMs*wheelSize),如果任务的延时超过了该时间范围(interval),如此时要插入一个延时为350ms的任务,则涉及到了层级时间轮,需要将该任务添加到上层时间轮中

第二层的时间轮的 tickMs 为第一层时间轮的 interval (20*1ms = 20ms),而每一层时间轮的 wheelSize 是固定的,都是20,因此第二层时间轮的时间跨度为 400ms,以此类推,第三层时间轮的时间跨度为8000ms

在这里插入图片描述
接下来看看延时350ms的任务的执行:

  1. 该任务被放到第二层时间轮的时间格17中 [340ms, 360ms)
  2. 时间运转到340ms时,该任务还剩下10ms 的时间,还不能执行这个任务的到期操作,因此会进行时间轮的降级
  3. 该任务会被降级到下层时间轮到期时间为 [10ms, 11ms)的时间格中
  4. 之后再经历10ms,任务真正到期,最终执行到期操作
4.时间轮的推进

接下来还存在一个问题,Kafka的时间是怎么推进的?如果类似采用 JDK 中的 scheduleAtFixedRate逐秒推进,显然是不合理的,这会造成很多时间帧的“空推进”,导致时间轮也失去了大部分的意义

Kafka中的定时器借助 JDK 中的 DelayQueue 来协助推进时间轮。其将每个使用到的 TimerTaskList 都加入 DelayQueue,DelayQueue会根据 TimerTaskList 对应的超时时间进行排序,最快到期的 TimerTaskList 会被排在队头。

Kafka 中有一个特定线程会去队头获取到期的 TimerTaskList ,进而对里面的 TimerTaskEntry 执行过期操作。

读到这里可能会困惑,开头明确指明了 DelayQueue 的时间复杂度太高,这里为何还要引入 DelayQueue呢。注意如果将TimerTaskEntry 直接插入 DelayQueue 中,性能显然是难以支撑的。而根据一定的规则将若干 TimerTaskEntry 划分到 TimerTaskList 这个组,再将 TimerTaskList 插入 DelayQueue,显然就大大降低了时间复杂度。且通过 DelayQueue推进时间又显著减少了时间的“空推进”,减少无故空耗机器的性能资源,通过以少量空间换时间做到“精准推进”。


http://chatgpt.dhexx.cn/article/6xsOVtIc.shtml

相关文章

时间轮算法(TimingWheel)

时间轮算法的应用非常广泛,在 Dubbo、Netty、Kafka、ZooKeeper、Quartz 的组件中都有时间轮思想的应用,甚至在 Linux 内核中都有用到。 其思想理论基础可以参考论文:http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pd…

那些惊艳的算法们(三)—— 时间轮

从定时任务说起 自然界中定时任务无处不在,太阳每天东升西落,候鸟的迁徙,树木的年轮,人们每天按时上班,每个月按时发工资、交房租,四季轮换,潮涨潮落,等等,从某种意义上…

1、时间轮

一、什么是时间轮? 作为一个粗人,咱不扯什么高级的词汇,直接上图: 上面是一张时间轮的示意图,可以看到,这个时间轮就像一个钟表一样,它有刻度,图中画了9个格子,每个格子…

浅谈时间轮算法

浅谈时间轮算法 基于队列的定时任务执行模型缺陷 在计算机世界中,只有待解决的问题变得大规模后,算法的价值才能够最大化的体现。时间轮算法可以将插入和删除操作的时间复杂度都降为 O(1),在大规模问题下还能够达到非常好的运行效果。 如果…

时间轮(TimingWheel)算法总结

通过阅读篇文章您可以很容易理解平时所使用的开源框架是如何进行任务调度的。而且对于以后业务上碰到需要做时间任务调度的需求,也可以尝试着用实践论算法去实现。 一、时间轮的应用 其实早在1987年,时间轮算法的论文已经发布。论文名称:Hash…

时间轮 (史上最全)

缓存之王 Caffeine 中,涉及到100w级、1000W级、甚至亿级元素的过期问题,如何进行高性能的定时调度,是一个难题。 注: 本文从 对 海量调度任务场景中, 高性能的时间轮算法, 做了一个 系统化、由浅入深的 穿透…

时间轮(Timing Wheel)案例和原理

什么是时间轮(Timing Wheel) 时间轮(Timing Wheel)是George Varghese和Tony Lauck在1996年的论文Hashed and Hierarchical Timing Wheels: data structures to efficiently implement a timer facility实现的,它在Linux内核中使用广泛,是Linux内核定时器…

时间轮(TimingWheel)

一、什么是时间轮 时间轮其实就是一种环形的数据结构,可以想象成时钟,分成很多格子,一个格子代表一段时间(这个时间越短,Timer的精度越高)。并用一个双向链表存储放在该格子上的延时任务,同时一…

高性能定时器3——时间轮

​ 在网络程序中我们通常要处理三种事件,网络I/O事件、信号以及定时事件,我们可以使用I/O复用系统调用(select、poll、epoll)将这三类事件进行统一处理。我们通常使用定时器来检测一个客户端的活动状态,服务器程序通常…

C#,斐波那契数列(Fibonacci Sequence)的八种算法与源代码

一、莱昂纳多斐波那契(Leonardo Fibonacci) 斐波那契公元1170年生于意大利比萨,卒于1250年,被人称作“比萨的莱昂纳多”,是一名闻名于欧洲的数学家,其主要的著作有《算盘书》、《实用几何》和《四艺经》等。…

matlab斐波那契数列画图,斐波拉契数列 斐波那契数列 matlab程序

斐波那契数列数列从第3项开始,每一项都等于前两项之和。 例子:数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368 应用: 生活斐波那契 斐波那契数列中的斐波脾气有点大,表面温和到别人误以为很好欺…

快速计算斐波那契数列(Fibonacci数列)

本文最后更新于 619 天前,其中的信息可能已经有所发展或是发生改变。 题目描述 输入一个正整数n,求Fibonacci数列的第n个数。Fibonacci数列的特点:第1,2个数为1,1。从第3个数开始,概述是前面两个数之和。即: 要求输入的…

递归求斐波那契数列

斐波那契数列 题目描述:编写一个函数,求斐波那契数列的第n项的值。 首先,对于斐波那契数列,我们是非常熟悉了,对斐波那契定义为如下:f(0)0,f(1)0,f(2)1,……f(n)f(n-1)f(n-2),其中n>1。对于这…

斐波那契数列(Fibonacci)

有一对兔子,出生后第3个月起每个月都生一对免子。小兔子长到第3个月后每个月又生一对兔子。假设所有兔子都不死,问40个月的免子总数为多少?解题思路: 这是一个有趣的古典数学问题。可以从表1看出兔子繁殖的规律。 …

【递归 动态规划 备忘录法】Fibonacci数列(斐波那契数列)(C++)

一、什么是Fibonacci数列 斐波那契数列指的是这样一个数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144…用文字来说,就是从第3项开始,每一项都等于前两项之和。至于包不包括0,一番查阅后没有得到证实… 不过重要的是“第3项开始…

算法-斐波那契数列Fibonacci

斐波那契数列 Fibonacci 斐波那契数列是这样的数列: 0、1、1、2、3、5, 8、13、21、34 …… 下一项是上两项的和。 2 是上两项的和(1+1) 3 是上两项的和(1+2)、 5 是(2+3)、 依此类推! 更多有意思的介绍可以见参考链接; 算法 1. 直接递归 初步想法就是采用递归的…

回顾斐波那契数列

一 概述 斐波那契数列(Fibonacci sequence),又称黄金分割数列。该数列是指递归方法定义为: F(0) 0,F(0) 1,F(n) F(n-1) F(n-2) (n ≥ 2,n ∈ N*) 的数列。 具体表现形式为:0&am…

题:斐波那契数列(Fibonacci数列)——一个数最少几步变成斐波那契数列的数

题目描述: Fibonacci数列就形如:0, 1, 1, 2, 3, 5, 8, 13, ...,在Fibonacci数列中的数我们称为Fibonacci数。给你一 个N,你想让其变为一个Fibonacci数,每一步你可以把当前数字X变为X-1或者X1,现在给你一个…

Fibonacci数列前20项(斐波那契数列)

HZKs Blog 本文标题: Fibonacci数列前20项(斐波那契数列) 本文链接: https://blog.zekun.fun/2021/coding/cppfibonacci数列前20项斐波那契数列/ 题目描述 Fibonacci数列的特点:第1,2个数为1,1。从第3个数开始,概述是前面两个数之…

for_each函数用法

Introduction 學習過STL的container後&#xff0c;想要存取每一個iterator&#xff0c;你一定寫過以下的程式 #include < vector > #include < iostream > using namespace std; int main() { int ia[] {1, 2, 3}; vector<int> ivec(ia, ia size…