首页 > 数据库 >Redis数据结构简介-Set

Redis数据结构简介-Set

时间:2022-11-10 17:24:52浏览次数:54  
标签:存储 Set score 元素 Redis 跳表 数据结构 节点

    Set 结构存储值与结构读写能力:   包含字符串的无序收集器(unordered collection), 且数据不重复.   添加,获取,移除单个元素; 检查一个元素是否存在于集合中; 计算交集,并集,差集; 从集合里面随机获取元素. 存储不可以重复的数据     ZSet(有序集合) 结构存储值与结构读写能力:   字符串成员(member)与浮点数分值(score)之间的有序映射, 元素的排列顺序由分值的大小决定.   添加,获取,删除单个元素; 根据分值范围(range)或者成员来获取元素. 存储有序的集合数据(set), Redis的有序集合就和散列一样, 都用于存储键值对: 有序集合的键被称为成员(member), 每个成员都是独一无二的; 而有序集合的值则被称为分值(score), 分值必须为浮点数. 有序集合是Redis里面唯一一个既可以根据成员(这一点和散列一样)访问元素, 又可以根据分值及分值的排列顺序来访问元素的结构.

 

    与 Java 中的 HashSet 一样,无序且存储元素不重复。其底层有两种方式实现,当 value是整数值时,且数据量是不大时使用 inset 来存储,其他情况都是用字典 dict(即hash) 来存储。     inset   Redis 中 inset 的结构定义如下所示:   
typedf struct inset{
    uint32_t encoding;//编码方式 有三种 默认 INSET_ENC_INT16
    uint32_t length;//集合元素个数
    int8_t contents[];//实际存储元素的数组 
                      //元素类型并不一定是ini8_t类型,柔性数组不占intset结构体大小,并且数组中的元
                      //素从小到大排列
}inset;
#define INTSET_ENC_INT16 (sizeof(int16_t))   //16位,2个字节,表示范围-32,768~32,767
#define INTSET_ENC_INT32 (sizeof(int32_t))   //32位,4个字节,表示范
                                             //围-2,147,483,648~2,147,483,647
#define INTSET_ENC_INT64 (sizeof(int64_t))   //64位,8个字节,表示范
//围-9,223,372,036,854,775,808~9,223,372,036,854,775,807

  

  编码格式 encoding:共有三种,INTSET_ENC_INT16、INSET_ENC_INT32 和 INSET_ENC_INT64 三种,分别对应不用的范围。Redis 为了尽可能地节省内存,会根据插入数据的大小选择不一样的类型来进行存储。   元素数量 length:记录了保存数据的数组 contents 中共有多少个元素,这样获取多个数的时间复杂度就是 O(1)。   数组 contents:真正存储数据的地方,数组时按照从小到大有序排列的,并且不包含任何重复项。     intset 的示意图如下所示:

 

    intset 中整数的升级过程   1. 了解旧的存储格式,计算出目前已有元素占用内存大小,计算规则是 length * encoding,如4 * 16 = 64。   2. 确定新的编码格式,当原有的编码格式不能存储下新增的数据时,此时就要选择新的合适的编码格式;   3. 根据新的编码格式计算出需要新增的内存大小,然后从尾部将数据插入。   4. 根据新的编码格式重置之前的值,此时 contents 存在两种编码格式设置的值,就需要进行统一,从插入新数据的起始位置开始,从后向前将之前的数据按照新的编码格式进行移动和设置。从后往前是为了防止数据被覆盖。   优点:根据存入的数据大小选择合适的编码方式,且只在必要的时候进行升级操作,节省内存。   缺点:升级过程耗费系统资源,还有就是不支持降级,一旦升级就不可降级。

 

     
    zset 是 Redis 提供的一个非常特殊的数据结构,常用作排行榜等功能,以用户 Id 为 value,关注时间或者分数作为 score 进行排序。与其他数据结构相似,zset也有两种不同的实现,分别是 zipList 和 skipList。   zipList:满足下面两个条件   1. {score, value} 键值对少于 128个   2. 每个元素的长度小于 64 字节     skipList:不满足以上两个条件时使用跳表、hash 和 skipList   1. hash 用来存储 value 到 score 的映射,这样就可以在 O(1) 时间内找到 value 对应的分数   2. skipList 按照从小到大的顺序存储分数。   3. skipList 每个元素的值都是 {score, value} 对。     只用 zipList 的示意图如下所示:

 

    使用跳表时的示意图:  

 

  跳表 skipList   跳表 skipList 在 Redis 中的运用场景只有一个,那就是作为有序列表 zset 的底层实现。跳表可以保证增删改查等操作时的时间复杂度为 O(logN),这个性能可以与平衡树相媲美,但实现方式上却更加简单,唯一美中不足的就是跳表占用的空间比较大,其实就是一种空间换时间的思想。跳表的结构如下所示:

 

  Redis 中的跳表一个节点最高可以达到 64层,一个跳表中最多可以存储 2^64个元素。跳表中,每个节点都是一个 skipListNode,每个跳表的节点也都维护者一个 score 值,这个值在跳表中是按照从小到大的顺序排列好的。
typedf struct zskiplist{
    	//头节点
    	struct zskiplistNode *header;
    	//尾节点
    	struct zskiplistNode *tail;
    	// 跳表中元素个数
    	unsigned long length;
    	//目前表内节点的最大层数
   	 int level;
}zskiplist;

  

    header:指向跳表的头节点,通过这个指针可以直接找到表头,时间复杂度为 O(1);   tail:指向跳表的尾节点,通过这个指针可以找到表尾,时间复杂度为O(1);   length:记录跳表的长度,即不包括头节点,整个跳表中有多少个元素;   level:记录当前跳表内,所有节点中层数最大的 level;     zskipListNode 的结构定义如下:
typedf struct zskiplistNode{
    	sds ele;// 具体的数据
    	double score;// 分数
    	struct zskiplistNode *backward;//后退指针
    	struct zskiplistLevel{  
        struct zskiplistNode *forward;//前进指针forward
        unsigned int span;//跨度span
    }level[];//层级数组 最大32
}zskiplistNode;

  

    ele:真正的数据,每个节点的数据都是唯一的,但节点的分数 score可以是一样的。两个相同分数 score 的节点是按照元素的字典序进行排序的;   score:各个节点中的数字是节点所保留的分数 score,在条表中,节点按照各自保存的分数从小到大排列;   backward:用于从表尾向表头便利,每个节点只有一个后退指针,即每次只能后退一步;   层级数组:这个数组中的每个节点都有两个属性,forward 指向下一个节点,span 跨度用来计算当前节点在跳表中的一个排名,这就为 zset 提供了一个查看排名的方法。数组中的每个节点中用1、2、3等字样标记节点的各个层,L1 代表第一层,L2代表第二层,L3代表第三次,以此类推;

 

 

 

   

标签:存储,Set,score,元素,Redis,跳表,数据结构,节点
From: https://www.cnblogs.com/qiezi777/p/16877729.html

相关文章

  • k8s03-Replication-Controller-ReplicaSet-有状态-无状态-daemonset
    ReplicationController和ReplicaSet无状态应用管理Deployment有状态应用管理StatefuSet守护进程集DaemonSetkubernets调度基础1.1RC和RSReplicationController......
  • Redis数据结构简介-Hash
     Hash结构存储值与结构读写能力:包含键值对的无序散列表添加,获取,移除单个键值对;获取所有键值对.存储类似HashMap的数据 hash是日常开发过......
  • Redis数据结构简介-List
     List结构存储值与结构读写能力:一个链表,链表上的每个节点都包含了一个字符串从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪(trim);读取单......
  • Redis 中 hash 扩容与缩容
    Redis中hash扩容与缩容当哈希表中元素数量逐渐增加时,此时产生hash冲突的概率逐渐增大,且由于dict也是采用拉链法解决hash冲突的,随着hash冲突概率上升,链表会越来越......
  • SpringBoot整合Redis(十九)
    二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。上一章简单介绍了多数据源配置MyBatisPlus(十八),如果没有看过,​​请观看上一章​​一.Redis的介绍和安装......
  • Redis模糊匹配删除key
    在群里看到的一个Redis快速删除数据小技巧。之前我一直用scan出来再删方式,比较慢,不如本文下面这个方法。造些测试数据foriin{1..1000};doecho"setage_$i$i"|re......
  • git 教程 --git reset 命令
    简介在程序员实际工作过程中,提交代码后,偶尔也有后悔的时候,但是都提交到本地仓库或者推送到远程服务器了,可怎么办?我想要回到某一个提交的版本,git就很贴心的提供了对应的功......
  • SpringBoot整合Redis_Jedis版(二十)
    二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。上一章简单介绍了SpringBoot整合Redis(十九),如果没有看过,​​请观看上一章​​SpringBoot2.0版本之后,采......
  • C语言里的几个拷贝函数memcpy、memset、strcpy、strncpy
    #include<string.h>1. src和dest所指内存区域不能重叠,函数返回指向dest的指针。memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度void*memc......
  • C# get、set 说明
    一、get、set的基本简介  在面向对象编程(OOP)中,是不允许外界直接对类的成员变量直接访问的,既然不能访问,那定义这些成员变量还有什么意义呢?所以C#中就要用set和get方法来......