再探Redis对象与底层数据结构的关系

大纲:简述Redis五种对象所使用的的底层数据结构

  • 字符串对象
  • 列表对象
  • 哈希对象
  • 集合对象
  • 有序集合对象

阅读本文你将收获什么:

  • 了解Redis五种对象的实现以及优点。
  • 了解对象系统设计上的优点。

简述:
上文我们已经了解过了Redis底层的六种数据结构,然而这六种数据结构咱们并不能直接使用,而是基于这些数据结构创建了Redis的对象系统,每种对象都用到了至少一种前文所述的数据结构。
这种以对象来实现方式的好处在于,可以根据对象的类型来判断对象是否可以执行给定的命令。另一个好处在于,我们可以针对不同的使用场景,为对象设置不同的数据结构实现,优化使用效率。

一。对象的编码与类型

图1·Redis对象与编码.png
图1·Redis对象与编码

图一表示了Redis与其对象系统,也就是咱们常用的五大类型,以及五大类型底层所有的编码方式。

对象的编码:

编码常量编码所对应的底层数据结构
REDIS_ENCODING_INTlong类型的整数
REDIS_ENCODING_EMBSTRembstr编码的简单动态字符串
REDIS_ENCODING_RAW简单动态字符串
REDIS_ENCODING_HT字典
REDIS_ENCODING_LINKEDLIST双端链表
REDIS_ENCODING_ZIPLIST压缩列表
REDIS_ENCODING_INTSE整数集合
REDIS_ENCODING_SKIPLIST跳跃表和字典

二.字符串对象

字符串对象的编码可以是int、raw、或者embstr。

  1. 如果一个字符串对象保存的是整数值,且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面,并且将字符串对象的编码设置为int。
    图2·int编码的字符串对象.png
    图2·int编码的字符串对象

  2. 如果字符串对象保存的是一个字符串值,且长度大于39字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个值,并将对象的编码设置为raw。
    图3·raw编码的字符串对象.png
    图3·raw编码的字符串对象

  3. 如果保存的字符串值的长度小于39字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。
    图4·embstr编码创建的内存块结构.png
    图4·embstr编码创建的内存块结构

使用embstr保存短字符串值的好处

  • embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次。
  • 释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的字符串对象需要调用两次。
  • 因为embstr编码的字符串对象所有的数据都保存在一块连续的内存里,所以这种编码的字符串对象比raw编码的字符串对象能够更好的利用缓存带来的优势。

Redis没有为embstr编码的字符串对象编写任何相应的修改程序,所以embstr编码的字符串对象实际上是只读的,任何对其修改的命令,程序都会将其编码从embstr转换成raw,在执行修改。

三.列表对象

列表对象的编码可以是ziplist或者linkedlist。

  1. ziplist编码的列表对象使用压缩列表作为底层实现,每个压缩列表节点(entry)保存了一个列表元素。

图5·ziplist编码的number列表对象.png
图5·ziplist编码的number列表对象

  1. 另一方面,linkedlist编码的列表对象使用双端链表作为底层实现,每个双端链表节点(node)都保存了一个字符串对象,每个字符串对象都保存了一个列表元素。

图6·linkedlist编码的列表对象.png
图6·linkedlist编码的列表对象

字符串对象是Redis五种类型的对象中唯一一种会被其他四种对象嵌套的对象。

编码转换
当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:

  • 列表对象保存的所有字符串长度都小于64字节;
  • 列表对象保存的元素数量小于512个。

四.哈希对象

哈希对象的编码可以是ziplist或者hashtable。

  1. ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表表尾,因此:
    • 保存了同一键值对的两个接地那总是紧挨在一起,保存键的节点在前,保存值的节点在后;
    • 先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向。

举例:
ziplist编码的哈希对象

图7·ziplist编码的哈希对象.png
图7·ziplist编码的哈希对象

压缩列表实现如下:

图8·哈希对象的压缩列表底层实现.png
图8·哈希对象的压缩列表底层实现

  1. hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都是用一个字典键值对来保存:
    • 字典的每个键都是一个字符串对象,对象中保存了键值对的键。
    • 字典的每个值都是一个字符串对象,对象中保存了键值对的值。

图9·hashtable编码的哈希对象.png
图9·hashtable编码的哈希对象

编码转换
当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
  • 哈希对象保存的键值对数量小于512个。

五.集合对象

集合对象的编码可以是intset或者hashtable。

  1. intset编码的集合对象使用整数集合作为底层实现,集合对象包含所有元素都被保存在整数集合里面。

图10·intset编码的集合对象.png
图10·intset编码的集合对象

  1. 另一方面,hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值全部被设置为NULL。

图11·hashtable编码的集合对象.png
图11·hashtable编码的集合对象

编码转换
当集合对象可以同时满足以下两个条件时,对象使用intset编码:

  • 集合对象保存的所有元素都是整数值;
  • 集合对象保存的元素数量不超过512个。

六.有序集合对象

有序集合的编码可以是ziplist或者skiplist。

  1. ziplist编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存分值。
    压缩列表内的几何元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的位置,而分值较大的元素则被放置在靠近表尾的位置。

图12·有序集合的压缩列表.png
图12·有序集合的压缩列表

  1. zset结构中的zsl跳跃表按照分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性则保存了元素的分值。通过这个跳跃表,程序可以对有序集合进行范围型操作。
    除此之外zset结构中的dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,值则保存了元素的分值。通过字典,程序可以以O(1)的复杂度查找给定成员的分值。

图13·skiplist编码的有序集合对象.png
图13·skiplist编码的有序集合对象

图14·有序集合元素同时被保存在字典和跳跃表中.png
图14·有序集合元素同时被保存在字典和跳跃表中

编码转换
当有序集合对象可同时满足以下两个条件时,对象使用ziplist编码:

  • 有序集合保存的元素数量小于128个;
  • 有序集合保存的所有元素成员的长度都小于64字节。

总结

以上就是Redis的对象系统如何使用底层基本数据结构来针对不同的场景进行实现。同时,经过分析,可以发现,Redis的对象系统的类型与编码组合使用以达到多种适用于不同类型, 不同长度数据存储的巧妙设计。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值