多图解释Redis的整数集合intset升级过程
redis源码分析系列文章
面试官:说说Redis的Hash底层 我:......(来自阅文的面试题)
前言
大噶好,今天仍然是元气满满的一天,抛开永远写不完的需求,拒绝要求贼变态的客户,单纯的学习技术,感受技术的魅力。(哈哈哈,皮一下很开森)
前面几周我们一起看了Redis底层数据结构,如动态字符串SDS
,双向链表Adlist
,字典Dict
,跳跃表
,如果有对Redis常见的类型或底层数据结构不明白的请看上面传送门。
今天来说下set的底层实现整数集合
,如果有对set不明白的,常见的API使用这篇就不讲了,看上面的传送门哈。
整数集合概念
整数集合是Redis设计的一种底层结构,是set的底层实现,当集合中只包含整数值元素,并且这个集合元素数据不多时,会使用这种结构。但是如果不满足刚才的条件,会使用其他结构,这边暂时不讲哈。
下图为整数集合的实际组成,包括三个部分,分别是编码格式encoding,包含元素数量length,保存元素的数组contents。(这边只需要简单看下,下面针对每个模块详细说明哈😝)
整数集合的实现
我们看下intset.h里面关于整数集合的定义,上代码哈:
编码格式encoding
包括INTSET_ENC_INT16,INTSET_ENC_INT32,INTSET_ENC_INT64三种类型,其分别对应着不同的范围,具体看上面代码的注释信息。
因为插入的数据的大小是不一样的,为了尽可能的节约内存
(毕竟都是钱,平时要省着点用😭),所以我们需要使用不同的类型来存储数据。
集合元素数量length
记录了保存数据contents的长度,即有多少个元素。
保存元素的数组contents
真正存储数据的地方,数组是按照从小到大
有序排序的,并且不包含任何重复项
(因为set是不含重复项,所以其底层实现也是不含包含项的)。
整数集合升级过程(重点,手动标星)
上面的图我们重新看下,编码格式encoding为INTSET_ENC_INT16,即每个数据占16位。长度length为4,即数组content里面有四个元素,分别是1,2,3,4。如果我们要添加一个数字位40000,很明显超过编码格式为INTSET_ENC_INT16的范围-32,768~32,767,应该是编码格式为INTSET_ENC_INT32。那么他是如何升级的呢,从INTSET_ENC_INT16升级到INTSET_ENC_INT32的呢?
1.了解旧的存储格式
首先我们看下1,2,3,4这四个元素是如何存储的。首先要知道一共有多少位,计算规则为length*编码格式的位数
,即4*16=64
。所以每个元素占用了16位。
2.确定新的编码格式
新的元素为40000,已经超过了INTSET_ENC_INT16的范围-32,768~32,767,所以新的编码格式为INTSET_ENC_INT32。
3.根据新的编码格式新增内存
上面已经说明了编码格式为INTSET_ENC_INT32,计算规则为length*编码格式的位数
,即5*32=160
。所以新增的位数为64-159。
4.根据编码格式设置对应的值
从上面知道按照新的编码格式,每个数据应该占用32位,但是旧的编码格式,每个数据占用16位。所以我们从后面开始,每次获取32位用来存储数据。
这样说太难懂了,看下图☺。
首先,那最后32位,即128-159存储40000。那么第49-127是空着的。
接着,取空着的49-127最后的32位,即96到127这32位,用来存储4。那么之前4存储的位置48-63
和49-127剩下的64-95
这两部分组成了一个大部分,即48-95
,现在空着啦。
在接着在48-95这个大部分,再取后32位,即64-95,用来存储3。那么之前3存储位置32-47
和48-95剩下的48-63
这两部分组成了一个大部分,即32-63
,现在空着啦。
再接着,将32-63这个大部分,再取后32位,即还是32-63,用来存储2。那么之前2存储位置16-31空着啦。
最后,将16-31和原来0-31合起来,存储1。
至此,整个升级过程结束。整体来说,分为3步,确定新的编码格式,新增需要的内存空间,从后往前调整数据。
这边有个小问题,为啥要从后往前调整数据呢?
原因是如果从前往后,数据可能会覆盖。也拿上面个例子来说,数据1在0-15位,数据2在16-31位,如果从前往后,我们知道新的编码格式INTSET_ENC_INT32要求每个元素占用32位,那么数据1应该占用0-31,这个时候数据2就被覆盖了,以后就不知道数据2啦。
但是从后往前,因为后面新增了一些内存,所以不会发生覆盖现象。
升级的优点
节约内存
整数集合既可以让集合保存三种不同类型的值,又可以确保升级操作只在有需要的时候进行,这样就节省了内存。
不支持降级
一旦对数组进行升级,编码就会一直保存升级后的状态。即使后面把40000删掉了,编码格式还是不会将会INTSET_ENC_INT16。
整数集合的源码分析
创建一个空集合 intsetnew
这个方法比较简单,是初始化整数集合的步骤,即下图部分。
主要的步骤是分配内存空间,设置默认编码格式,以及初始化数组长度length。
添加元素并升级insetAdd流程图(重点)
添加元素并升级insetAdd源码分析
可以根据上面的流程图,对照着下面的源码分析,这边就不写啦哈。
结语
该篇主要讲了Redis的SET数据类型的底层实现整数集合,先从整数集合是什么,,剖析了其主要组成部分,进而通过多幅过程图解释了intset是如何升级的,最后结合源码对整数集合进行描述,如创建过程,升级过程,中间穿插例子和过程图。
如果觉得写得还行,麻烦给个赞👍,您的认可才是我写作的动力!
如果觉得有说的不对的地方,欢迎评论指出。
好了,拜拜咯。