Redis对象系统及数据结构 笔记

key-value实现

Redis并不是直接通过基本的数据结构来实现键值对数据库的,而是基于这些数据结构实现了一个对象系统,包括了字符串对象、集合对象、哈希对象、有序集合、列表对象

每当我们在数据库中添加一个键值对时,会至少新建两个对象:

  1. 键值对的是一个对象,该对象总是为字符串对象
  2. 键值对的是一个对象,可以任意的基本数据结构。

对象的结构如下:
在这里插入图片描述

内存回收

Redis使用了引用计数法来对对象进行回收。

  • 创建一个对象时,计数置为1
  • 当对象被使用时,计数会加一
  • 当对象不被使用时,计数会减一
  • 当对象的计数为0,内存会被释放

对象共享

当键A和键B的值对象相同时,那么他们的值指针会指向同一个值对象。被共享的值对象的引用计数会加一。共享对象节约了内存,同时,共享对象不止可以被字符串键使用,在数据结构中嵌套了字符串对象的数据结构也能使用共享对象,如LinkedList编码的列表对象、HashTable编码的哈希对象与集合对象、zset编码的有序集合对象。

注意:只有当共享对象和键想创建的值对象完全相同时,该共享对象才会作为该键的值对象。而且共享对象只能是保存整数的字符串对象。因为整数字符串对象验证的时间复杂度是O(1),其他数据结构验证起来时间复杂度过高,如字符串可达O(N),列表或哈希对象可能达O(N^2)。

对象的空转时长

对象中的 lru 属性记录了对象最后一次被访问的时间。空转时长就是 当前的时间减去lru的时间 得出的。当开启了 maxmemory 选项且选择了 LRU 的内存回收算法,当超过 maxmemory 阈值时,那么空转时长较大的键会被优先释放,以回收内存。

基本数据结构

字符串

在这里插入图片描述

压缩列表

他是列表键和哈希键的底层实现之一。列表键使用压缩列表作为存储的条件如下:

  1. 只包含少量列表项
  2. 每个列表项是较小的整数值或长度较短的字符串

使用压缩列表可以节约内存,但是只能通过遍历去获取对象,当对象太多时效率会变低,所以在符合一定条件的情况下,可以使用压缩列表,此时对效率影响不大且能节约内存。当键不符合上述条件时,即对象太多或列表项太大,会使用其他编码方式来保存列表项,以提高读取、写入效率。

基本结构

每个压缩列表节点可以保存一个整数值或一个字节数组。其结构如下:
在这里插入图片描述

  • previous_entry_length:保存了前一个节点的长度。用当前节点的指针p减去该长度就可定位到上一个节点的起始位置。压缩列表就是使用这种方法来从表尾向表头遍历。
  • encoding:记录节点所保存的数据类型和长度。
  • content:保存的节点的值,即整数值或字符数组。

连锁更新

简而言之,就是当插入一个新节点时,该节点的长度超过了后一个节点的 previous_entry_length 所能表示的范围(超过了1字节),那么此时后一个节点的previous_entry_length属性就需要扩展空间,而后一个节点的接着下一个节点previous_entry_length也会由此而需要扩展空间,继而导致了连锁效应,使得新节点后面的所有节点都需要进行空间扩展。最坏时间复杂度是O(N^2)。

当然,实际情况中由于连锁更新导致的性能低下的概率是很小的,主要是因为:

  1. 需要压缩列表里有多个连续的长度与250~253字节的节点,实际情况不多见。
  2. 即使出现了连锁更新,当只要更新的节点数量不多,那么对性能不会造成影响。

跳跃表

其实就是一种可以进行二分查找的有序链表,是一种有序的数据结构,每个节点都有多个指向其他节点的指针,以达到快速访问的目的。平均时间复杂度是O(logN),最差是O(N)。它是有序集合的底层实现之一。

实现

Redis中由zskiplistNode表示跳跃表节点,zskiplist保存跳跃表节点的相关信息。
在这里插入图片描述
在查找时,从上层指针开始查找,找到对应的区间之后再到下一层去查找,直到找到该节点。

zskiplist包含以下信息:
在这里插入图片描述
跳跃表节点
在这里插入图片描述

  • 层:level数组可以包含多个元素,元素中的指针指向其他节点。程序通过这些层来提高访问其他节点的速度,一般来说,层数越多,访问的速度越快。
    每次创建新的节点时,会利用幂次定律为该节点随机生成一个层数作为数组的大小。
  • 跨度:即level[i].span,用来记录两个节点间的距离。跨度是用来计算排位(rank)的:将沿途访问过的所有层的跨度累加起来,就是目标节点在跳跃表中的排位。
  • 分值和成员:节点的score属性即分值,跳跃表中的节点是按照分值从小到大排序的。成员对象obj是一个指针,指向字符串对象,其中保存着SDS值。

与红黑树等平衡树相比,跳跃表具有以下优点:

  1. 插入速度非常快速,因为不需要进行旋转等操作来维护平衡性;
  2. 范围查询更具优势,跳表可直接往后遍历,而红黑树最差还要回溯到根节点
  3. 更容易实现;

有序集合使用哈希表和跳跃表实现。使用哈希表是为了能在O(1)时间复杂度内查找到成员对象对应的分值score。跳跃表可以提高范围查询的效率,如ZRANKZRANGE

参考资料

  1. 《Redis设计与实现》
  2. 《Redis深度历险》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值