二、六大数据结构

简单动态字符串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节点之间使用双向指针串接起来

posted @   上好佳28  阅读(19)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示