美文网首页
Redis ZSet底层实现以及ZSet实现延时队列

Redis ZSet底层实现以及ZSet实现延时队列

作者: HannahLi_9f1c | 来源:发表于2020-05-08 22:50 被阅读0次

将知识从定义、来源、实现、问题、优化、应用方面来系统性的回答

Zset原理

有序集合对象是有序的。与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score)作为排序依据

ZSet底层如何实现

一、使用ziplist。
  1. 前提:保存元素数量小于128,并且每个元素长度小于64字节
    (这两个参数可以通过zset-max-ziplist-entries 选项和 zset-max-ziplist-value 进行修改)
  2. ziplist原理:
    压缩列表(ziplist)是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。
    image
image
  • 每个节点组成如图。previous_entry_length保存前一个节点的长度,遍历时可根据定位到前一个节点。encoding存储content的类型和长度。content保存节点的内容
二、使用字典和跳跃表
typedef struct zset{
     //跳跃表
     zskiplist *zsl;
     //字典
     dict *dice;
} zset;

字典的键保存元素的值,字典的值则保存元素的分值;跳跃表节点的 object 属性保存元素的值,跳跃表节点的 score 属性保存元素的分值。

为什么不直接用跳跃表

假如我们单独使用 字典,虽然能以 O(1) 的时间复杂度查找成员的分值,但是因为字典是以无序的方式来保存集合元素,所以每次进行范围操作的时候都要进行排序;假如我们单独使用跳跃表来实现,虽然能执行范围操作,但是查找操作有 O(1)的复杂度变为了O(logN)。因此Redis使用了两种数据结构来共同实现有序集合。

字典

字典中的键是唯一的,可以通过key来查找值

  • 字典底层实现是哈希表,字典有两个哈希表,一个在扩容时使用,哈希表扩容使用渐进式扩容,发送扩容时需要在两个哈希表中进行搜索。


    image
  • 发生哈希冲突时使用链地址法解决
跳跃表

跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其它节点的指针,从而达到快速访问节点的目的。


image
  1. 性质
  • 由很多层结构组成
  • 每一层都是一个有序的链表,排列顺序为由高层到底层,都至少包含两个链表节点,分别是前面的head节点和后面的nil节
  • 最底层的链表包含了所有的元素
  • 如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现(上一层的元素是当前层的元素的子集)
  • 链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点;
  1. 定义
typedef struct zskiplistNode {
     //层
     struct zskiplistLevel{
           //前进指针
           struct zskiplistNode *forward;
           //跨度
           unsigned int span;
     }level[];
 
     //后退指针
     struct zskiplistNode *backward;
     //分值
     double score;
     //成员对象
     robj *obj;
 
} zskiplistNode
typedef struct zskiplist{
     //表头节点和表尾节点
     structz skiplistNode *header, *tail;
     //表中节点的数量
     unsigned long length;
     //表中层数最大的节点的层数
     int level;
 
}zskiplist;
  1. 操作
  • 搜索:从最高层的链表节点开始,如果比当前节点要大和比当前层的下一个节点要小,那么则往下找,也就是和当前层的下一层的节点的下一个节点进行比较,以此类推,一直找到最底层的最后一个节点,如果找到则返回,反之则返回空。
  • 插入:首先确定插入的层数,有一种方法是假设抛一枚硬币,如果是正面就累加,直到遇见反面为止,最后记录正面的次数作为插入的层数。当确定插入的层数k后,则需要将新元素插入到从底层到k层。
  • 删除:在各个层中找到包含指定值的节点,然后将节点从链表中删除即可,如果删除以后只剩下头尾两个节点,则删除这一层。

  1. ZSet延时队列如何实现
    Java延时队列

本文参考:

https://www.cnblogs.com/ysocean/p/9080942.html#_label3

相关文章

  • Redis ZSet底层实现以及ZSet实现延时队列

    将知识从定义、来源、实现、问题、优化、应用方面来系统性的回答 Zset原理 有序集合对象是有序的。与列表使用索引下...

  • 通过redis的有序集合[zset] 实现延迟队列

    php使用redis的有序集合zset实现延迟队列 我们通过redis的有序集合zset来实现简单的延迟队列,将消...

  • Redis3.2源码分析-跳跃表zskiplist

    跳跃表是Redis zset的底层实现之一,zset在member较多时会采用跳跃表作为底层实现,它在添加、删除、...

  • 使用redis实现一个简单的延时消息队列

    延时消息队列可以使用redis的zset来实现,将消息序列化为一个字符串作为zset的value,消息到期时间作为...

  • 跳跃表python实践

    redis的zset常用场景:统计日活;打赏排行榜;天梯榜等zset底层基于跳跃表实现 跳跃表代码实现,练习版

  • Redis ZSet底层实现

    Zset原理 有序集合对象是有序的。与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score...

  • t_zset.c

    Redis的t_zset.c是对zset数据结构的实现。zset是由 dict 和zskiplist来实现的。 当...

  • 使用 Redis 的 zset 实现延时队列

    Java 延时队列DelayQueue实现原理及Demo[http://mp.weixin.qq.com/s?__...

  • (3)zset实现延时队列(2)

    1、场景: 订单超时未支付,取消订单,恢复库存 创建的订单加入redis延时队列,单独线程轮循处理过期订单,如已支...

  • redis 使用场景

    场景一、基于 zset、set 实现抽奖 场景二、基于 zset 延迟任务 场景三、基于 list 消息队列 ...

网友评论

      本文标题:Redis ZSet底层实现以及ZSet实现延时队列

      本文链接:https://www.haomeiwen.com/subject/opdcnhtx.html