基于 golang 从零到一实现时间轮算法 (一)

前言

时间轮是用来解决海量百万级定时器(或延时)任务的最佳方案,linux 的内核定时器就是采用该数据结构实现。


应用场景

  1. 自动删除缓存中过期的 Key:缓存中设置了 TTL 的 kv,通过把该 key 对应的 TTL 以及回调方法注册到 timewheel,到期直接删除
  2. 延时任务,将任务注册到 timewheel,过期自动触发执行
  3. 在 TcpServer 中,用来管理海量 Tcp 连接的超时定时器,如 zinx 的定时器 实现

简单时间轮

在这里插入图片描述
如上图,一个普通的时间轮,类似于时钟表盘,指针(pointer)每隔一段时间前进一格(interval,tick 一次),一圈代表一个周期(circle),定时任务以链表(双向)方式置放在表盘的刻度处,当指针前进到当前位置时,遍历任务链表,执行相应的任务。

基本模型构成

  1. tickMs(基本时间跨度):时间轮由多个时间格组成,每个时间格代表当前时间轮的基本时间跨度(tickMs)。
  2. wheelSize(时间单位个数):时间轮的时间格个数是固定的,可用(wheelSize)来表示,那么整个时间轮的总体时间跨度(interval)可以通过公式tickMs × wheelSize计算得出。
  3. currentTime(当前所处时间):时间轮还有一个表盘指针(currentTime),用来表示时间轮当前所处的时间,currentTime是 tickMs 的整数倍。currentTime 可以将整个时间轮划分为到期部分和未到期部分,currentTime当前指向的时间格也属于到期部分,表示刚好到期,需要处理此时间格所对应的 TimerTaskList 的所有任务。

从开发角度而言,实现一个时间轮:

  1. 时间轮是一个由固定长度 length 的数组(本例子中就是 [1,12])构造而成的环形队列
  2. 时间轮的长度决定了延时任务的刻度,假设上面的刻度为 1s(即时间轮 1s 前进一格),那么该时间轮只能表达延时任务在 1s 至 12s内的任务;时间轮的长度也即时间轮的周期(12s)
  3. 注册任务按照 当前刻度 + 延时时长 % 时间轮周期 计算得出,假设当前指针在 5s的位置,此时添加一个延时周期为 5s 的任务,那么该任务需要注册到刻度为 10s 的格子对应的任务链表中
  4. 数组中的每个元素都指向一个双向链表,用于存储对应的延时任务
  5. 时间轮的插入复杂度是 O(1),删除指定节点的复杂度是O(n),因为需要遍历双向链表以查找到要删除的节点
  6. 当时间轮指针转动到对应的单元格时,顺序执行双向链表中存储的任务基础时间轮的缺点是无法注册延时超过时间轮周期的任务,如何解决呢?

基础时间轮的缺点是无法注册延时超过时间轮周期的任务,如何解决呢?

解决方法 1:加 circle 计数器

此方法相当于给双向链表中存储的任务加多一个 “圈数” 的维度
在这里插入图片描述
• 假设时间轮有8个刻度
• 计时器值 = 17
• 走两圈时间轮+多走1个刻度
• 在"0"这个桶中设置计时器
• 与计时器一同保留#圈数
• 在到期处理时,如果#圈数 > 0,则重新插入计时器

注意,此策略中,时间轮指针每前进一格,需要把此格对应的任务链表中,所有的任务的 circle 计数器都减 1,如果 circle==0,那么说明,任务时间已到期,执行该任务

解决方法 2:层级时间轮

这是一种典型的 “空间换时间” 的思路,按照时间轮周期的倍数进行合理分层,有两个优点:

  1. 避免任务堆积在某个 slot 上
  2. 支持任意长时间的延时任务注册
    在这里插入图片描述
    复用之前的案例,第一层的时间轮 tickMs=1ms, wheelSize=20, interval=20ms。第二层的时间轮的 tickMs 为第一层时间轮的 interval,即为 20ms。每一层时间轮的 wheelSize 是固定的,都是 20,那么第二层的时间轮的总体时间跨度 interval 为 400ms。以此类推,这个 400ms 也是第三层的 tickMs 的大小,第三层的时间轮的总体时间跨度为 8000ms。

关于层级时间轮,可以参考普通的时间。多层时间轮的概念也非常清晰,将时间轮分为多个,每两个轮之间是进位的关系,例如最普遍的秒,分,时.即:

  • 秒级时间轮上,设有60个槽, 每两个槽之间的时间为1s.
  • 分钟级时间轮上,设有60s个槽,每两个槽之间的时间为1min
  • 小时级时间轮上,设有24个槽,每两个槽之间的时间为1h
    在这里插入图片描述
    这样,秒级每走60个槽,时间过去一分钟,秒级时间轮归零,分级时间轮走一个槽; 分级时间轮每走过60个槽,时间过去一小时,分级时间轮归零,小时级时间轮走一个槽.

通过三级时间轮,只需要遍历60+60+60 =180个槽,就可以成为一个精度为1s, 周期为60x60x60 = 216000s的定时调度器.


参考

https://zhuanlan.zhihu.com/p/658079556,
https://pandaychen.github.io/2022/05/28/A-TIMEWHEEL-ANALYSIS/
https://juejin.cn/post/7083795682313633822

属于在以上博客基础上记录的个人笔记。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/159694.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL索引与优化篇】数据库的设计规范

数据库的设计规范 文章目录 数据库的设计规范1. 范式2. 键和相关属性的概念3. 第一范式4. 第二范式5. 第三范式6. 小结7. 反范式化7.1 概述7.2 反范式的新问题7.3 反范式适用场景 8. 巴斯范式9. 第四范式、第五范式和域键范式 1. 范式 在关系型数据库中,关于数据表…

51单片机复位电容计算与分析(附带Proteus电路图)

因为iC x (dU/dt).在上电瞬间,U从0变化到U,所以这一瞬间就是通的,然后这就是一个直流回路,因为电容C直流中是断路的,所以就不通了。 然后来分析一下这个电容的电压到底是能不能达到单片机需要的复位电压。 这是一个线性电容&…

数据分析师的就业前景看好

IT行业哪个方向比较好就业? 目前IT行业的发展速度非常的快,很多领域都已经开始和IT技术进行紧密的结合,这也带来了很多岗位的空缺。那么就一起来说说,IT行业里,哪些方向会更好就业。 大数据时代,呼唤数据分析师。 …

压缩软件 7-Zip VS WinZips?

7-zip在联想应用商店给强烈推荐? 要说它好用还行,但每次压缩都显示网络连接失败等异常广告信息。 相反好用的7-ZIP必须鼠标点击右键点击更多才能够看到,这次更新体验也太差了吧? 用户放在第一位? 要不是更新后一直推…

EMC Unity存储系统如何查看SSD的使用寿命

为什么要写这个博客? 客户对老的EMC unity的存储系统要扩容,如何确定SSD磁盘是全新的还是拆机二手的?很多时候客户还有一个奇葩的要求,就是要和5年前的磁盘PN一致,甚至要求固件版本一致,最关键的还要求是全…

C++二分查找算法的应用:长度递增组的最大数目

本文涉及的基础知识点 二分查找 题目 给你一个下标从 0 开始、长度为 n 的数组 usageLimits 。 你的任务是使用从 0 到 n - 1 的数字创建若干组,并确保每个数字 i 在 所有组 中使用的次数总共不超过 usageLimits[i] 次。此外,还必须满足以下条件&…

学Python,一个月从小白到大神?看你怎么学!

Python是一门超强大而且超受欢迎的编程语言。它被用在各种领域,比如网站开发、数据分析、人工智能和机器学习。学会Python会给你创造很多职业机会,所以绝对是值得一试的。 但你有没有过这样的梦想:一个月时间,从Python小白变成Py…

【C语言初学者周冲刺计划】2.4求一个3×3矩阵对角线元素之和

1解题思路&#xff1a; 首先学会怎么输入3*3矩阵的数值&#xff1b;然后细节处&#xff0c;比如最后求和应该加什么&#xff0c;怎么写问题&#xff1b; 2代码&#xff1a; #include<stdio.h> int main() {int str[3][3],i,j,sum0;for(i0;i<3;i){for(j0;j<3;j){…

CodeWhisperer 的使用心得

文章作者&#xff1a;小SS 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最前沿技术&#xff0c;观点&#xff0c;和项目&#xff0c;并将中国优秀开发者或技术推荐给全球云社…

解决方案中word中分节符的使用

解决方案中必不可少的两个“符号”&#xff0c;分页符&#xff0c;分节符 有了分节符&#xff0c;可以为不同节设置不同的页眉页脚、分栏格式、纸张大小及方向、页边距、不同节间采用不同的页码序号&#xff0c;常用的功能主要是把word下一次的由原来的“竖版”&#xff0c;变…

openGauss学习笔记-112 openGauss 数据库管理-管理用户及权限-行级访问控制

文章目录 openGauss学习笔记-112 openGauss 数据库管理-管理用户及权限-行级访问控制 openGauss学习笔记-112 openGauss 数据库管理-管理用户及权限-行级访问控制 行级访问控制特性将数据库访问控制精确到数据表行级别&#xff0c;使数据库达到行级访问控制的能力。不同用户执…

「Java开发指南」如何用MyEclipse搭建Spring MVC应用程序?(一)

本教程将指导开发者如何生成一个可运行的Spring MVC客户应用程序&#xff0c;该应用程序实现域模型的CRUD应用程序模式。在本教程中&#xff0c;您将学习如何&#xff1a; 从数据库表的Scaffold到现有项目部署搭建的应用程序 使用Spring MVC搭建需要MyEclipse Spring或Bling授…