Redis5中数据结构底层实现

Redis5中数据结构底层

https://juejin.cn/post/6844904008591605767

1. String

SDS ( simple dynamic string ), 注意英文原意。 结构如下

struct sdshdr {

    // 记录 buf 数组中已使用字节的数量
    // 等于 SDS 所保存字符串的长度
    int len;

    // 记录 buf 数组中未使用字节的数量
    int free;

    // 字节数组,用于保存字符串
    char buf[];

};

image-20220208190232340

优点:

  1. 常数复杂度获取字符串长度

  2. 惰性空间释放, 如果字符串缩短, 先不释放,而是记录在free中, 待下次直接使用

  3. 二进制安全, redis通过len来判断字符长度,而不是 \0结束符,这使得redis不仅可以保存文本数据,还可以保存任意格式的二进制数据

image-20220208190838274

链接:https://zhuanlan.zhihu.com/p/51075839

扩容分析:

整体思路:

1 惰性缩容。不释放空间,留给到期释放等机制释放。

2 加倍扩容。在需要空间达1M之前按新空间两倍分配空间,否则按新空间大小+1M分配。注意,1M=10241024Char。Char可以是5bits/8bits/16bits/32bits/64bits

2. ziplist 压缩列表

redis的列表键和哈希键的底层实现之一

当一个列表键只包含少量列表项, 并且每个列表项要么就是小整数值, 要么就是长度比较短的字符串, 那么 Redis 就会使用压缩列表来做列表键的底层实现。

另外, 当一个哈希键只包含少量键值对, 并且每个键值对的键和值要么就是小整数值, 要么就是长度比较短的字符串, 那么 Redis 就会使用压缩列表来做哈希键的底层实现。

image-20220208191931262

每个压缩列表节点都由 previous_entry_lengthencodingcontent 三个部分组成

image-20220208192428989

节点的 previous_entry_length属性以字节为单位,记录了压缩列表中前一个节点的长度。 previous_entry_length属性的长度可以是1字节或者5字节。

  • 如果前一节点的长度小于254字节,那么 previous_entry_length属性的长度为1字节,前一节点的长度就保存在这一个字节里面。
  • 如果前一节点的长度大于等于254字节,那么 previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度

节点的encoding属性记录了节点的content属性所保存数据的类型以及长度。

  • 一字节、两字节或者五字节长,值的最高位为00、01或者10的是字节数组编码这种编码表示节点的 content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录。

  • 一字节长,值的最高位以11开头的是整数编码:这种编码表示节点的content属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录。

    节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定

参考:https://www.cnblogs.com/hunternet/p/11306690.html

5. Zset,有序列表

  1. 什么是跳表?

  2. 跳表查找、插入、删除元素的流程 和 时间复杂度

    【查找】查到是趋于二分查找,时间复杂度 O(log(n))

    【插入】插入的同时需要维护索引, 维护的时候有一个随机函数randomLevel()。 该函数需要确保,第二层的节点数是最底层的1/2, 第三层的节点数是最底层的1/4。 层级最高是32,每次随机一个值,然后以随机值所在的层,往下每层都加入元素

  3. 插入元素的时候,如何动态维护索引

总结:

  1. 跳表是可以实现二分查找的有序链表;
  2. 每个元素插入时随机生成它的level;
  3. 最底层包含所有的元素;
  4. 如果一个元素出现在level(x),那么它肯定出现在x以下的level中;
  5. 每个索引节点包含两个指针,一个向下,一个向右;(笔记目前看过的各种跳表源码实现包括Redis 的zset 都没有向下的指针,那怎么从二级索引跳到一级索引呢?留个悬念,看源码吧,文末有跳表实现源码)
  6. 跳表查询、插入、删除的时间复杂度为O(log n),与平衡二叉树接近;

为什么redis使用跳表 而不是红黑树来实现有序集合?

  1. Redis 中的有序集合(zset) 支持的操作:

    1. 插入一个元素
    2. 删除一个元素
    3. 查找一个元素
    4. 有序输出所有元素
    5. 按照范围区间查找元素(比如查找值在 [100, 356] 之间的数据)

    其中,前四个操作红黑树也可以完成,且时间复杂度跟跳表是一样的。但是,按照区间来查找数据这个操作,红黑树的效率没有跳表高。按照区间查找数据时,跳表可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了,非常高效。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值