Redis数据类型底层详解、跳表查询

一、Redis 数据类型相关

数据类型实际描述的是 value 的类型,key 都是 string,常见数据类型(value

        1.stringembstrrawint

        2.listquicklist(多个 ziplist 双向链表))

        3.hashziplisthashtable

        4.setintsethashtable

        5.sorted setziplistskiplist

        6.bitmap

        7.hyperloglog

每一种类型都用 redisObject 结构体来表示,每种类型根据情况不同,有不同的编码 encoding(即底层数据结构)

二、String

1.如果字符串保存的是整数值,则底层编码为 int,实际使用 long 来存储

2.如果字符串保存的是非整数值(浮点数字或其它字符)又分两种情况

        1.长度 <= 39 字节,使用 embstr 编码来保存,即将 redisObject sdshdr 结构体保存在一起,分配内存只需一次

        2.长度 > 39 字节,使用 raw 编码来保存,即 redisObject 结构体分配一次内存,sdshdr 结构体分配一次内存,用指针相连

3.sdshdr 称为简单动态字符串,实现上有点类似于 java 中的 StringBuilder,有如下特性

        1.单独存储字符长度,相比 char* 获取长度效率高(char* C语言原生字符串表示)

        2.支持动态扩容,方便字符串拼接操作

        3.预留空间,减少内存分配、释放次数(< 1M 时容量是字符串实际长度 2 倍,>= 1M 时容量是原有容量 + 1M

        4.二进制安全,例如传统 char* \0 作为结束字符,这样就不能保存视频、图片等二进制数据,而 sds 以长度来进行读取

三、List

1.3.2 开始,Redis 采用 quicklist 作为其编码方式,它是一个双向链表,节点元素是 ziplist

        1.由于是链表,内存上不连续

        2.操作头尾效率高,时间复杂度 O(1)

        3.链表中 ziplist 的大小和元素个数都可以设置,其中大小默认 8kb

2.ziplist 用一块连续的内存存储数据,设计目标是让数据存储更紧凑,减少碎片开销,节约内存,它的结构如下

        1.zlbytes记录整个 ziplist 占用字节数

        2.zltail-offset – 记录尾节点偏移量

        3.zllength记录节点数量

        4.entry – 节点,1 ~ N 个,每个 entry 记录了前一 entry 长度,本 entry 的编码、长度、实际数据,为了节省内存,根据实际数据长度不同,用于记录长度的字节数也不同,例如前一 entry 长度是 253 时,需要用 1 个字节,但超过了 253,需要用 5 个字节

        5.zlend结束标记

3.ziplist 适合存储少量元素,否则查询效率不高,并且长度可变的设计会带来连锁更新问题

四、Hash

1.在数据量较小时,采用 ziplist 作为其编码,当键或值长度过大(64)或个数过多(512)时,转为 hashtable 编码

2.hashtable 编码

        hash 函数,Redis 5.0 采用了 SipHash 算法

        采用拉链法解决 key 冲突

rehash 时机
当元素数 < 1 * 桶个数时,不扩容
当元素数 > 5 * 桶个数时,一定扩容
1 * 桶个数 <= 元素数 <= 5 * 桶个数时,如果此时没有进行 AOF RDB 操作时
当元素数 < 桶个数 / 10 时,缩容

                 rehash 要点

每个字典有两个哈希表,桶个数为 2 𝑛 2^n, 平时使用 ht [0] ht [1] 开始为 null ,在扩容时新数组大小为元素个数 * 2
渐进式 rehash ,即不是一次将所有桶都迁移过去,每次对这张表 CRUD 仅迁移一个桶
active rehash server 的主循环中,每 100 ms 里留出 1s 进行主动迁移
rehash 过程中,新增操作 ht [1] ,其它操作先操作 ht [0] ,若没有,再操作 ht [1]
redis 所有 CRUD 都是单线程,因此 rehash 一定是线程安全的

五、Sorted Set

1.在数据量较小时,采用 ziplist 作为其编码,按 score 有序,当键或值长度过大(64)或个数过多(128)时,转为 skiplist + hashtable 编码,同时采用的理由是

        只用 hashtable,CRUD O(1),但要执行有序操作,需要排序,带来额外时间空间复杂度

        只用 skiplist,虽然范围操作优点保留,但时间复杂度上升

        虽然同时采用了两种结构,但由于采用了指针,元素并不会占用双份内存

2.skiplist (跳表)要点:多层链表、排序规则、 backwardlevelspanforward

        整个跳表是有序的,下图按照score排好序,如果score,再按其他数据排。backward可以用于倒叙遍历。level是层级,每个数据层级可能不一样,level里面有两个属性,forward是前向指针,span用于计算排名,记录两个节点之间的跨度

六、跳表查询

skiplist 查找要点,从顶层开始

        1.> 右边的,继续向右

        2.= 找到了

        3.< 右边的或右边为 NULL,下一层,重复 12 步骤

以查找 score = 22 为例,虽然跳表是有序的,但是底层是链表结构,不能使用二分法。多层链表就是为了减少比较次数,提高效率。先从第四层开始,同层的右侧元素null,则下一层,第三层,和右侧元素37比,下一层,第二层>右侧元素,向右小于37,下一层就找到22

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠哉iky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值