定时器的实现: 1.跳表 2.红黑树 3.时间轮

gcc skiplist.c sl-timer.c  -I./ -o sl-timer
./sl-timer

g++ test.cc -o test
./test

1)红黑树

2)时间轮

3)跳表
    zset   数据>=128时,数据用skiplist来存储。

    redis有哪些事件需要定时器处理?
        key expire
        
        redis的是双向 链表的实现(无序的)。 
            优化: 
                1.有序
                2.跳表     CRUD的时间复杂度: 大概率的趋向于O(Logn)
                             理想: 是O(Logn)


                rank: 需要支持反向查找。

    跳表: 第一个节点就是最近要过期的节点。

    查找和插入,删除(任意节点和头结点) O(Logn) 

    过期时间,只需要跟第一个节点比较。

============================
最小堆(Mark不太认可):  特征: 父节点比子节点小.  子节点之间无顺序性能.
    二叉树: boost.asio

    四叉树: go


=======================定时器的作用
1.超时任务

2.定时任务

===================具备的数据特性

需要有序性。

查找和插入数据速度要快(时间复杂度)。

同时要保证结构有序性。

===================开发
第一步: 简单可用,持续优化.


==================学习源码:需要去看数据结构,自己构架框架的时候,可以直接从源码中获取=============
做高性能中间件,框架的时候:
    时间和空间

=======定时器的接口====
init_timer: 初始化定时器  ==》初始化            初始化一个跳表结构
add_timer: 添加定时器     ==》添加定时器        添加一个节点。 同时,根据当前时间+超时时间,插入一个节点。
del_timer: 删除定时器     ==》删除定时器        删除一个节点。
expire_timer: 超时       ==》定时器过期函数     死循环:每次找最小的,第一个都没过期,则跳出循环。第一个过期,则回调函数,并删除节点。 

=======
实际过程,是epoll_wait阻塞的, 先处理网络事件,再调用超时。 是用IO多路复用去阻塞的。


---------------------------------红黑树的实现
nginx中,红黑树: 稳定的O(Logn)。    跳表是:不稳定的O(Logn)。     最小堆:子节点是无序的,因此不合适。  时间轮在游戏中是最好的。

二叉树的问题: 删除的时候,容易将二叉树退化为单链表==》
    AVL: 为了解决这个问题,出现了AVL这种平衡的二叉树,左右子树的高度相差为1。但是平衡操作太重度了。
    红黑树: 每次插入都是以红色节点来插入的。 只需要维护一个 ‘黑高度’ ,让黑高度一致即可。只用做简单的旋转,就做到了平衡,是一种简化版本的AVL。

红黑树也是一种有序的数据结构。
    
从左往右依次增大。


红黑树,只用找最左边的节点。因为它最小。  但是成千、上百万的定时器的话。 性能不如:跳表。

哨兵节点: 黑高度。 任意一个子节点触发到最上层节点的高度。

红黑树 + 哨兵 

skynet: 10ms

值相同的话,是由插入函数决定的: 是更新呢? 还是新建一个节点。

stl: map、set 也是红黑树, 但是key相同时,是更新。 而实现定时器,新加节点。

相邻的节点不能都是红节点,否则会:触发旋转操作。

--------------------时间轮------------
跳表 和 红黑树 在多线程环境下,应该如何加锁?

互斥锁和自旋锁的区别:

一个需求:
    假设检测连接超时,根据心跳包来检测,10s钟没收到,就断开连接。
    redis的主从节点、哨兵节点、zookeeper 都是10s。
    openresty nginx+lua==》  红黑树来检测
    客户端每5s发送一次。

一般的做法: map<fd, conn*>, 每s轮训这个结构,检查所有的连接是否超时。
    收到心跳包就记录下时间戳。 

    问题:效率非常差。 每次对所有的连接都检查。

    分治的思路:只检测快要过期的链接,不检测。  数组+链表,hash,设置为10还是16?

    相同秒数过期的,则加成链表链接起来。

redis hash: 为什么设置为2的N次方。 取余数的时候,设置为2的n次方,取余数是很快的。

时间定时器:
    先确定精度:10ms 改为1ms, skynet源码 质量非常高的定时器。

每个时间轮,里面都是链表。

缺点: 因为总是移动的,不容易删除(如: 引用计数删除.  第二种:不处理)。
        像时钟一样运行,确保每个精度运行1次。
        
优点:锁的粒度小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值