二、六大数据结构
简单动态字符串SDS
-
杜绝缓冲区溢出(SDS会先扩展再修改)
-
减少字符串操作中的内存重分配次数(预分配和惰性清除)
-
二进制安全(二进制处理,使用len属性而非空格来判断字符串是否结束)
链表
指针相连,空间不连续,天然支持扩容,插入删除操作高效(时间复杂度为O(1)),但缺点是随机访问低效(时间复杂度为O(N))
redis使用的是双向无环链表,带头指针和尾指针,带链表长度计数器,可以保存各种不同类型的值
字典
Redis字典使用散列表作为底层实现
Redis如何解决散列冲突
1、链表法
哈希表采用链表法解决散列冲突,被分配到同一个地址的键会构成一个单向链表
2、rehash机制
为了保证负载因子维持在一个合理的范围,当散列表内的键值对过多或过少时,需要定期进行rehash
每个字典有两个哈希表,一个是正常使用,一个用于rehash期间使用:字典只使用ht[0] 哈希表, ht[1]哈希表只会对ht[0]哈希表进行rehash时使用
rehash过程:
1、为ht[1]分配空间
-
扩展操作:ht[1]的大小为 第一个大于等于ht[0].used*2的2的n次方幂。如:ht[0].used=3则ht[1]的大小为8,ht[0].used=4则ht[1]的大小为8。
-
收缩操作: ht[1]的大小为 第一个大于等于ht[0].used的2的n次方幂
2、将保存在ht[0]中的键值对重新计算键的散列值和索引值,然后放到ht[1]指定的位置上
3、将ht[0]包含的所有键值对都迁移到了ht[1]之后,释放ht[0],将ht[1]设置为ht[0],并创建一个新的ht[1]哈希表为下一次rehash做准备
渐进式rehash
集中一次做数据迁移比较耗时,生产环境不能接受
将扩容操作穿插在插入操作的过程中,分批完成
所以在渐进式 rehash 进行期间,字典的删除(delete)、查找(find)、更新(update)等操作会在两个哈希表上进行,新添加到字典的键值对一律会被保存到 ht[1] 里面,而 ht[0] 则不再进行任何添加操作:这一措施保证了 ht[0] 包含的键值对数量会只减不增,并随着 rehash 操作的执行而最终变成空表
跳跃表
因为单链表查找速度太慢了,所以当链表元素过多时就会采用跳跃表,就是有多层索引
在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的,跳跃表中的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序
每次创建一个新的跳跃链表节点时,程序都会根据幂次定律(powerlaw,越大的数出现的概率越小)随机生成一个介于1和32之间的值作为level,即层的高度
整数集合
redis自己设计的用于保存整数值的数据结构
-
整数集合的底层实现为数组,这个数组以有序、无重复的方式保存集合元素,在有需要时,程序会根据新添加元素的类型,改变这个数组的类型(int16_t/int32_t/int64_t)
-
升级操作为整数集合带来了操作上的灵活性,并且尽可能地节约了内存
-
整数集合只支持升级操作,不支持降级操作
压缩列表
redis为节省内存自己设计的顺序型数据结构
类似数组,通过一片连续的内存空间,来存储数据。不过,它跟数组不同的一点是,它允许存储的数据大小不同。
数组要求每个元素的大小相同,如果我们要存储不同长度的字符串,那我们就需要用最大长度的字符串大小作为元素的大小,会浪费存储空间
压缩列表给每个节点增加一个长度属性,在遍历节点的时候就知道每个节点的长度(占用内存的大小),就可以很容易计算出下一个节点再内存中的位置
快速链表
quicklist 实际上是 zipList 和 linkedList 的混合体,它将 linkedList 按段切分,每段就是一个quicklist节点,每一段使用 zipList 来紧凑存储,多个quicklist节点之间使用双向指针串接起来
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】