redis设计与实现学习---(一)基础数据类型

一 基础数据类型

1 简单动态字符串(simple dynamic string,SDS)

与C中字符串的异同点:
① 获取字符串长度,C需要遍历字符数组,而SDS则通过len记录
② 杜绝缓冲区溢出,C中如果合并两个字符串字符数组之前没有分配足够的空间就会造成数据截断,丢失数据,而SDS则合并之前判断free是否足够,不足够则会扩容在合并
③ 因为有预留的free空间,所以在修改字符串的时候会减少内存重分配的次数(默认SDS长度小于1M则预分配free=len,如果大于1M则free=1M)。
④ 惰性的内存重分配,在SDS的len减少的时候不会直接缩小总内存,而是在需要增加时直接使用或者通过需要提供API手动释放
⑤ SDS是二进制安全的,C字符串必须要有编码,而且除了末尾字符中间不能有空字符(空字符意味着字符传的结束,SDS有len所以无限制)
⑥ 因为保留了一些与C相同的结构(字符数组存储并且尾部都是\0),所以部分的C函数可以直接使用,而不需要完全重写
在这里插入图片描述
在这里插入图片描述

2 链表

redis链表实现基本都是双端链表,无环。
在这里插入图片描述
在这里插入图片描述

3 字典

redis字典索引位置存放与JDK相同,都是hash&(length-1)
redis使用的hash算法是MurmurHash
redis相同hash的键值链表采用的都是头插法
redis扩容时并不是一次性的把所有元素挪到新的字典中,而是先创建一个新的字典,然后字典中维护一个索引计数器变量,初始值为0。对字典进行操作时,会把索引计数器上的键值对都挪到新的字典中,并且将索引计数器+1,当所有旧字典的键值都转移之后索引计数器变为-1,扩容结束。

4 跳表

redis跳表层高32,并且多维护一个跨度,指的是与本层的前驱节点中间间隔实际节点数量,这样通过计算查找过程中每个遇到的节点的跨度就可以知道当前节点在总的底层节点的序号。

5 整数集合

整数集合可以保存int16 int32 int64的整数值,并且可以保证集合中不出现重复的元素。
整数集合的升级:
① 根据新元素的类型,扩展整数集合底层数组的空间大小,并为新元素分配空间。
② 将底层数组现有的所有元素都转换成与新元素相同的类型,并将类型转换后的元素 放置到正确的位上,而且在放置元素的过程中,需要继续维持底层数组的有序性质不变。
③ 将新元素添加到底层数组里面。
注意:
因为引发升级的新元素的长度总是比整数集合现有所有元素的长度都大,所以这个 新元素的值要么就大于所有现有元素,要么就小于所有现有元素,存放位置要么表头,要么表位。
整数集合不会降级
在这里插入图片描述

6 压缩列表

当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。
压缩列表组成:
在这里插入图片描述

① zlbytes 四字节,记录整个压缩列表所占用的内存字节数
② zltail 四字节,记录尾部节点距离起始地址有几个字节,可以快速取到尾节点(不是zlend,而是最后一个entry的起始地址)
③ zllen 二字节,记录的压缩列表包含的节点数量(因为是2字节所以最大存储65535,如果真实数量大于65535时需要遍历才能得到)
④ zlend 一字节,固定的0xff(255),标记压缩列表末端

压缩列表节点组成:
在这里插入图片描述

① previous_entry_length 可以是一字节或者五字节,记录前一个节点的长度,通过这个可以快速计算前一个结点的起始地址。如果前一节点的长度大于等于254字节,那么previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度。
② encoding一字节,二字节或者五字节,记录了节点的content属性所保存数据的类型以及长度。
如果值的最高位为00、01或者10的是字节数组编码,这种编码表示节点的content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录。
如果值的最高位以11开头(整数编码只可能是1个字节的)表示整数编码,这种编码表示节点的content属性保存着 整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录;
在这里插入图片描述
在这里插入图片描述

③ content属性负责保存节点的值,长度由encoding排除前两位得来
压缩列表的缺点:
假设原始都采用1字节来记录前一个结点的大小,突然往第一个节点前面插入了一个特别大的字符串,那么会导致后一个节点的previous_entry_length用一个字节保存不下了,需要变成5个字节,接着第三个也记录不下了导致连锁更新。不过这种极端情况一般很少出现,可以在redis配置中更改最大元素数量。

7 对象

Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了 一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象 这五种类型的对象,每种对象都用到了至少一种数据结构。
在这里插入图片描述

类型:TYPE KEY
在这里插入图片描述

编码:OBJECT ENCODING KEY
在这里插入图片描述
在这里插入图片描述

8 多态命令的实现

DEL、EXPIRE等命令对每个对象都可以使用,SET等只能对某种对象做操作,主要就是根据redisObject中的type做的区分,而SET等命令的具体操作实现则是根据encoding做的方法重载。

9 内存回收

redisObject结构中还有一个refcount属性,记录被引用的个数,初始为1,每次引用或者取消引用对应+1-1,变为0时内存会被释放。OBJECT REFCOUNT KEY可以查看某个KEY的引用数量。

10 共享对象

共享对象只能是1-9999之间的整数,是redis启动时自动创建的,类似于Integer中128以内的数字都是全局共享对象。1-9999中OBJECT REFCOUNT KEY的结果为固定的2147483647,10000及以上则是真实数量。
为什么不共享字符串或者大对象:
由于大对象判断相等时CPU耗费比较多,所以权衡只做10000以内的数字共享。

11 对象的空转时长

redisObject的lru属性,该属性记录了对象最后一次被命令程序访问的时间。当配置缓存淘汰策略时这个是主要依据

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值