目前为止,我们介绍了 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. vue实现rsa加密,数字签名,md5加密等

    一.使用jsencrypt进行rsa加密 原文链接:Js参数RSA加密传输,jsencrypt.js的使用 - CSDN博客 *(原文处有一个地方不对,不需要转换+,rsa已经做过base64转码了) ...

  2. Hadoop 在 windows 7 64位的配置(二)|非cygwin

    第一次使用需要 hdfs namenode -format 一键启动和关闭hadoop 新建文本文档 然后改名 start-hadoop.cmd 里面的内容 @echo off cd /d %HADO ...

  3. Blazor之ABC

    .net core正式推出了Blazor, 正好在一个小项目里试用一下. 建立项目 因为基于WebAssemble的客户端Blazor还不是正式版, 为了避免不必要的麻烦,我试用服务端的Blazor. ...

  4. 2018.8.20 Python之路---常用模块

    一.re模块 查找: re.findall(‘正则表达式’,‘字符串’) 匹配所有符合正则表达式的内容,形成一个列表,每一项都是列表中的一个元素. ret = re.findall('\d+','sj ...

  5. Pycharm 专业版激活码(转) 有效期到2020/06月

    亲测有效!!! 有效期截止为2020年06月,多谢大家支持与讨论! K6IXATEF43-eyJsaWNlbnNlSWQiOiJLNklYQVRFRjQzIiwibGljZW5zZWVOYW1lIjo ...

  6. Flask:Flask中使用会话技术

    1.会话技术cookie和session (1)cookie 在网站中,HTTP请求是无状态的.也就是说,即使第一次用户访问服务器并登录成功后,第二次请求服务器依然不知道当前发起请求的是哪个用户.co ...

  7. Java后端开发工作 - 写接口

    我在公司的工作内容是,对于一个BS应用,负责服务器端开发工作,Java语言.与前端开发人员合作,最终提供给前端RESTFUL接口,保证页面正常响应. 经验之谈 一个接口可以理解为一个业务逻辑,一个业务 ...

  8. Jenkins流水线获取提交日志

    写在前 之前使用Jenkins pipeline的时候发现拿不到日志,使用multiple scms插件对应是日志变量获取日志的方式失效了, 但是查看流水线Pipeline Syntax发现check ...

  9. vsftpd超实用技巧详解

    简介: vsftpd是"very secure FTP daemon"的缩写,是一个完全免费的.开放源代码的ftp服务器软件. 工作原理: vsftpd使用ftp协议,该协议属于应 ...

  10. MySQL批量插入的分析以及注意事项

    目录 1.背景 2.两种方式对比 2.1.一次插入一条数据 2.2.一次插入多条数据 3.拓展一下 4.Other 1.背景 我们在工作中基本都会碰到批量插入数据到DB的情况,这个时候我们就需要根据不 ...