SDS的数据结构:
capacity 和 len两个属性都是泛型,为什么不直接用int类型?因为Redis内部有很多优化方案,为更合理的使用内存,不同长度的字符串采用不同的数据类型表示,且在创建字符串的时候 len 会和 capacity 一样大,不产生冗余的空间,所以String值可以是字符串、数字(整数、浮点数) 或者二进制。
redis中SDS和C语言字符串对比:
1)C语言中的字符串,遇到’\0’则结尾,用长度N+1的数组维护长度为N的字符串。
Redis的SDS是:
len表示字符串的长度;
free表示空闲的,未分配的空间;
buffer数组是真正的字符串,并且以’\0’结尾。
2)C 字符串并不记录自身的长度信息,获取一个C字符串的长度,必须遍历整个字符串,对遇到的字符进行计数,直到遇到代表字符串结尾的空字符为止,复杂度为O(n)
SDS 在len属性中记录了SDS的本身长度,复杂度为O(1)
3)C字符串不记录自身长度容易造成缓冲区溢出
SDS 的空间分配策略完全杜绝了发生缓冲区的可能性:当SDS API 需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作,所以使用SDS既不需要修改SDS的空间大小,也不会出现前面所说的缓冲区溢出问题
4)减少修改字符串时带来的内存重分配次数
C字符串的长度和底层数组的长度之间存在这种关联性,所以每次增长或者缩短一个C字符串,总要对保存C字符串的数组进行一次内存重分配操作
在SDS中,buf数组的长度不一定就是字符数量加一,数组里面可以包含为使用的字节,而这些字节的数量就由SDS的free属性记录。通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略
5)二级制安全
C字符串必须符合某种编码,并且除了字符串的末尾之外,字符串里面不能包含空字符
SDS的API都是二进制安全的,所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据
6)C兼容所有字符串函数
SDS兼容部分C字符串函数
String类型的应用
1、可以存储base64的图片数据
2、作为缓存功能,降低mysql数据库的请求
3、做一些短时间的错误限制控制
二、List
:列表类型
Redis中的list本质是链表结构
list 的实现在3.2版本之前有两种方式:
压缩列表ziplist
双向链表linkedlist
在3.2版本之后引入了:
快速列表quicklist
因为双向链表linkedlist占用的内存比压缩列表ziplist要多, 所以当创建新的列表键时,列表会优先考虑使用压缩列表ziplist, 并且在有需要的时候, 才从压缩列表ziplist实现转换到双向链表linkedlist实现。
而后续引入的quicklist可以看作是linkedlist和ziplist的结合体。
这三种列表内部使用哪一种类型是通过编码来区分的:
linkedlist
linkedlist是一个双向列表,每个节点都会存储指向上一个节点和指向下一个节点的指针。linkedlist因为每个节点的空间是不连续的,所以可能会造成过多的空间碎片。
linkedlist的存储结构,链表中每一个节点都是一个listNode对象(源码adlist.h内),不过需要注意的是,列表中的value其实也是一个字符串对象。
然后会将其再进行封装成为一个list对象(源码adlist.h内):
Redis中对linkedlist的访问是以NULL值为终点的,因为head节点的prev节点为NULL,tail节点的next节点为NULL。
所以,在Redis3.2之前我们可以得到如下简图:
ziplist
ziplist是为了节省内存而开发的一种压缩列表数据结构,哈希数据类型底层也用到了ziplist。
ziplist是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个ziplist可以包含任意多个entry,而每一个entry又可以保存一个字节数组或者一个整数值。
ziplist和linkedlist最大的区别是ziplist不存储指向上一个节点和下一个节点的指针,存储的是上一个节点的长度和当前节点的长度,牺牲了部分读写性能来换取高效的内存利用率,是一种时间换空间的思想。
ziplist适用于字段个数少和字段值少的场景。
ziplist的组成结构:
关于列表选择使用ziplist编码进行存储,必须满足下面两个条件:
1)列表对象保存的所有字符串元素的长度都小于64字节。
2)列表对象保存的元素数量小于512个。
一旦不满足这两个条件中的任意一个,则会使用linkedlist编码来进行存储
这两个条件可以通过参数list-max-ziplist-value和list-max-ziplist-entries进行修改重新设定
quicklist
在Redis3.2之后,统一用quicklist来存储列表对象,quicklist存储了一个双向列表,每个列表的节点是一个ziplist,所以实际上quicklist就是linkedlist和ziplist的结合。
quicklist内部存储结构,quicklist中每一个节点都是一个quicklistNode对象,其数据结构定义为:
然后各个quicklistNode就构成了一个列表,quicklist:
根据上面两个结构图,可以得到Redis3.2之后列表的简图:
quicklist和原始两种列表的对比
quicklist同样采用了linkedlist的双端列表特性,然后quicklist中的每个节点又是一个ziplist,所以quicklist就是综合平衡考虑了空间碎片和读写性能两个维度。
使用quicklist需要注意以下2点:
1、如果ziplist中的entry个数过少,极端情况就是只有1个entry,此时就相当于退化成了一个普通的linkedlist。
2、如果ziplist中的entry过多,那么也会导致一次性需要申请的内存空间过大,而且因为ziplist本身的就是以时间换空间,所以会过多entry也会影响到列表对象的读写性能。
ziplist中的entry个数可以通过参数list-max-ziplist-size来控制:
list-max-ziplist-size 1
这个参数可以配置正数也可以配置负数。正数表示限制每个节点中的entry数量,如果是负数则只能为-1~-5
-1:每个ziplist最多只能为4KB
-2:每个ziplist最多只能为8KB
-3:每个ziplist最多只能为16KB
-4:每个ziplist最多只能为32KB
-5:每个ziplist最多只能为64KB
Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。其中 0 表示列表的第一个元素,1 表示列表的第二个元素,以此类推。也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
List数据类型应用:
timeline:例如微博的时间轴,有人发布微博,用lpush加入时间轴,展示新的列表信息。
三、Hash
:哈希表类型
Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。
Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)
Redis的哈希对象的底层存储可以使用ziplist(压缩列表)和hashtable。
当hash对象可以同时满足以下两个条件时,哈希对象使用ziplist编码
1.哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
2.哈希对象保存的键值对数量小于512个
常见操作命令:
所有hash的命令都是h开头的 hget、hset、hdel等
Hash数据类型应用:
1、缓存:能直观,相比string更节省空间的维护缓存信息,如:用户信息,视频信息等。
四、Set
:无序集合类型
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,将一个重复的元素添加到set中将会被忽略。
集合对象的编码可以是 intset 或者 hashtable。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
3079776)]
[外链图片转存中…(img-ldK29sbS-1714633079776)]
[外链图片转存中…(img-mst7vMz2-1714633079776)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新