目前为止,我们介绍了 redis 中非常典型的五种数据结构,从 SDS 到 压缩列表,这都是 redis 最底层、最常用的数据结构,相信你也掌握的不错。

但 redis 实际存储键值对的时候,是基于对象这个基本单位的,并且往往一个对象下面对对应不同的底层数据结构实现以便于在不同的场景下切换底层实现提升效率。例如列表对象在元素不多情况话会使用压缩列表来实现以压缩内存,而在元素比较多的时候常规的双端链表进行实现。

下面我们就具体来看看 redis 中都有哪些对象,底层又对应哪些可供选择的数据结构。

一、Redis 对象结构定义

redis 为每个对象定义为如下数据结构:

typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS;
int refcount;
void *ptr;
} robj;

type 记录的是当前的对象类型,有以下几种类型:

#define OBJ_STRING 0   /*字符串对象*/

#define OBJ_LIST 1    /*列表对象*/

#define OBJ_SET 2    /*集合对象*/

#define OBJ_ZSET 3   /*有序集合对象*/

#define OBJ_HASH 4   /*哈希对象*/

encoding 记录的是当前对象使用的哪种底层数据结构实现的,有以下类型可供选择:

#define OBJ_ENCODING_RAW 0     /* SDS 字符串 */

#define OBJ_ENCODING_INT 1     /* 整数 */

#define OBJ_ENCODING_HT 2      /* 字典结构 */

#define OBJ_ENCODING_ZIPMAP 3  /* 压缩map,已经废弃 */

#define OBJ_ENCODING_LINKEDLIST 4 /* LinkedList 双端链表,废弃了 */

#define OBJ_ENCODING_ZIPLIST 5 /* 压缩列表 */

#define OBJ_ENCODING_INTSET 6  /* 整数集合 */

#define OBJ_ENCODING_SKIPLIST 7  /* 跳跃表 */

#define OBJ_ENCODING_EMBSTR 8  /* 短字符串 */

#define OBJ_ENCODING_QUICKLIST 9 /* 压缩链表和双向链表组成的快速列表 */

8 和 9 我们遇到时在介绍,这里暂时不做介绍。

lru 记录的是上一个当前对象实例被访问的时间,它用作计算对象空转时长,空转时长过大的对象会被 redis 优先释放内存。

refcount 记录的是对象的引用计数,引用计数算法是很多编程语言中管理对象是否应该被销毁的依据,和它类似的典型的 Java 中可达性分析算法,都是用于计数当前对象是否依然被使用,以便释放内存。

ptr 指针指向的是实际实现当前对象的数据结构首地址。

以上就是 redisObject 数据结构的基本解释,下面我们看具体的对象分别会在什么情况下切换不同的底层实现。

二、字符串对象

字符串对象有三种 encoding 值,也就是只有这三种情况,redis 才会使用字符串对象存储数据。

  1. 字符串(raw):普通的字符串
  2. 整数(int):long 类型的整数值
  3. 短字符串(embstr ):短字符串

如果判定使用 raw 编码,那么 redis 的 ptr 指针将会指向一个 SDS 结构,如果确定使用 int 编码,那么会将 redisObject 中 ptr 类型由 void* 变成 long,继而分配 robj 内存。

当字符串的长度小于 39 个字节时,会采用 embstr 这种编码,embstr 其实也是使用 SDS 进行存储,区别于 raw 编码的是,后者会将 robj 和 ptr 指向的 SDS 分配在连续的内存块,唯一的好处是分配和释放内存都只需要一次操作即可完成,再一个是因为数据相邻,有可能一次加载 robj 的时候,CPU 将后面的 embstr 也加载进缓存,等到访问的时候就可以直接从缓存中访问。

但是,我们看一个例子:

hello 原本是以 int 编码存储的,但是我们执行 append 命令添加了字符串之后,它变成了 raw 编码。

这其实是 redis 的一种编码换换,当 hello 不再适合使用 int 编码继续存储的时候,会进行一个编码转换。

三、列表对象

列表对象有两种编码,压缩列表 ziplist 和 linkedlist。我们之前说过压缩列表的推荐应用场景,少量整数或字符串的时候可以用压缩列表来节省内存空间,而大数据量的节点则推荐使用普通的双端链表进行实现。

但是实际上,redis 的较新版本已经使用一种叫 quicklist 的快速列表整合 ziplist 和 linkedlist 作为列表对象的实现了。它将所有的节点分段拆分,每一份又使用压缩列表进行压缩,不同段之间使用双向指针连接。

四、集合对象

集合对象也有两种编码,整数集合 intset 和 字典 hashtable。默认情况下,当集合中有且仅有整型数据,且不超过 512 个,那么 redis 会使用整数集合 intset 进行集合存储,其余情况 redis 则构建字典进行集合数据存储。

顺便给大家复习下 intset 的无重复性、顺序性的特性,重复的元素是插入不进去的,因为插入之前会通过二分查找查找是否存在该元素,如果存在则拒绝插入操作。

当然了,如果集合中元素个数超过 512,那么 redis 就转而使用字典结构进行数据存储,具体实例就不再演示了。

五、有序集合对象

有序集合对象同样使用两种编码 ziplist 和 skiplist,可能你又见到压缩列表的身影了,足以见得,压缩列表是一个非常优秀的数据结构。

同样,当有序集合中包含少量元素的时候,redis 会优先使用压缩列表进行存储,反之选择跳跃表。

sadd 命令的标准语法是:

ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN

每一个元素都会对应一个分值,skiplist 本身的实现就需要这个分值进行元素的存储排序,有的时候有序集合会使用压缩列表进行实现,那么也需要这个分值来有序的压缩元素,这也是压缩列表页可以实现有序集合的原因。

这里补充一下,虽然说 redis 的有序集合是跳表实现的,这句话不错,但有失偏驳。

typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;

准确来说,redis 中的有序集合是由我们之前介绍过的字典加上跳表(组合起来就是zset)实现的,字典中保存的数据和分数 score 的映射关系,每次插入数据会从字典中查询,如果已经存在了,就不再插入,有序集合中是不允许重复数据。

六、哈希对象

哈希对象的编码可以是 ziplist 或者 hashtable,没什么特殊的,不再赘述。

以上,我们总结了 redis 中五大对象结构,以及他们可选的底层实现数据结构,相信你也理解的不错,这将非常有助于我们后面的学习。

下节开始,我们向 redis 数据库迈进~


关注公众不迷路,一个爱分享的程序员。

公众号回复「1024」加作者微信一起探讨学习!

每篇文章用到的所有案例代码素材都会上传我个人 github

https://github.com/SingleYam/overview_java

欢迎来踩!

Redis 的底层数据结构(对象)的更多相关文章

  1. Redis(二)--- Redis的底层数据结构

    1.Redis的数据结构 Redis 的底层数据结构包含简单的动态字符串(SDS).链表.字典.压缩列表.整数集合等等:五大数据类型(数据对象)都是由一种或几种数结构构成. 在命令行中可以使用 OBJ ...

  2. 深入理解Redis:底层数据结构

    简介 redis[1]是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorte ...

  3. Redis详解(四)------ redis的底层数据结构

    上一篇博客我们介绍了 redis的五大数据类型详细用法,但是在 Redis 中,这几种数据类型底层是由什么数据结构构造的呢?本篇博客我们就来详细介绍Redis中五大数据类型的底层实现. 1.演示数据类 ...

  4. Redis 详解 (四) redis的底层数据结构

    目录 1.演示数据类型的实现 2.简单动态字符串 3.链表 4.字典 5.跳跃表 6.整数集合 7.压缩列表 8.总结 上一篇博客我们介绍了 redis的五大数据类型详细用法,但是在 Redis 中, ...

  5. redis string底层数据结构sds

    redis的string没有采用c语言的字符串数组而采用自定义的数据结构SDS(simple dynamic string)设计 len 为字符串的实际长度  在redis中获取字符串的key长度的时 ...

  6. Redis 的底层数据结构(SDS和链表)

    Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件.可能几乎所有的线上项目都会使用到 Redis,无论你是做缓存.或是用作消息中间件,用起来很简单方便 ...

  7. Redis 的底层数据结构(字典)

    字典相对于数组,链表来说,是一种较高层次的数据结构,像我们的汉语字典一样,可以通过拼音或偏旁唯一确定一个汉字,在程序里我们管每一个映射关系叫做一个键值对,很多个键值对放在一起就构成了我们的字典结构. ...

  8. Redis 的底层数据结构(跳跃表)

    字典相对于数组,链表来说,是一种较高层次的数据结构,像我们的汉语字典一样,可以通过拼音或偏旁唯一确定一个汉字,在程序里我们管每一个映射关系叫做一个键值对,很多个键值对放在一起就构成了我们的字典结构. ...

  9. Redis 的底层数据结构(整数集合)

    当一个集合中只包含整数,并且元素的个数不是很多的话,redis 会用整数集合作为底层存储,它的一个优点就是可以节省很多内存,虽然字典结构的效率很高,但是它的实现结构相对复杂并且会分配较多的内存空间. ...

随机推荐

  1. 使用 pdf.js 跨域问题的处理方法1

    在<使用 pdf.js 在网页中加载 pdf 文件>中详细介绍了 pdf.js 的使用与集成网页开发的基本方法.展示效果如下图: 站点的目录为 http://localhost:8033/ ...

  2. 让SpringBoot的jackson支持JavaBean嵌套的protobuf

    问题背景 REST 项目使用protobuf 来加速项目开发,定义了很多model,vo,最终返回的仍然是JSON. 项目中一般使用 一个Response类, public class Respons ...

  3. 防抖与节流 & 若每个请求必须发送,如何平滑地获取最后一个接口返回的数据

    博客地址:https://ainyi.com/79 日常浏览网页中,在进行窗口的 resize.scroll 或者重复点击某按钮发送请求,此时事件处理函数或者接口调用的频率若无限制,则会加重浏览器的负 ...

  4. Java设计模式_七大原则

    简介 单一职责原则.对类来说,即一个类应该只负责一项职责. 开闭原则.对扩展开放,对修改关闭.在程序需要进行扩展的时候,不能去修改原有代码,使用接口和抽象类实现一个热插拔的效果. 里氏替换原则.任何基 ...

  5. dbms_job基础

    a.创建job: dbms_job.submit(jobno,what,next_date,interval);b.删除job: dbms_job.remove(jobno); c.修改要执行的操作: ...

  6. ManyToMany 字段的使用

    创建一个经典的多对多关系:一本书可以有多个作者,一个作者可以有多本书(如下,csdn复制的图片) 当进行数据迁移时,会生成三张表,了解就好 1,查询数据的操作 : 1.一本书的所有作者 b = Boo ...

  7. Linux nfs服务介绍

    nfs服务介绍 nfs(Network File System) 网络文件系统,能使用户访问服务器的文件系统,就像访问自己的本机的文件系统一样,并且多个 客户端共享访问该文件系统. 目前nfs服务,较 ...

  8. 创建WebApi

    一.创建 Web 项目 使用vs创建项目,选择“ASP.NET Core Web 应用程序”模板,将项目命名为 TodoApi,然后单击“确定”. 在“新建 ASP.NET Core Web 应用程序 ...

  9. SpringMVC----执行流程+底层解析

    SpringMVC流程图如上面所示,根据上图,串联一下底层源码: 1.在DispatcherServlet中找到doDisPatch 2.观察方法体,然后找到getHandler方法 3.点进方法,发 ...

  10. [考试反思]1023csp-s模拟测试83:等候

    分数倒是依旧那么烂,但是这个时间比较诡异. 6分49秒弄出T1,15分钟送上T2的50分暴力,不到一小时半的时候T3的30分暴力也完成了... 在85分钟之后一次提交也没有 前15分钟平均每分钟得10 ...