目录

前言

1、对象的类型与编码

1.1、类型

2、编码和底层实现

2.1、字符串对象

2.2、列表对象

2.3、哈希对象

2.4、集合对象

2.5、有序集合对象

参考

最后


前言

Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,每种对象都用到了至少一种我们前面所介绍的数据结构。

Redis数据结构——整数集合-intset

Redis数据结构——字典-hashtable

Redis数据结构——跳跃表-skiplist

Redis数据结构——链表-linkedlist

Redis数据结构——简单动态字符串-SDS

通过这五种不同类型的对象可以为我们带来的好处之一是:redis可以在执行命令之前,根据对象的类型来判断一个对象是否可以执行给定的命令;使用对象的另一个好处是,我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。

除此之外,redis的对象系统还实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象所占用的内存就会被自动释放;另外,redis还通过引用计数技术实现了对象共享机制,这一机制可以在适当条件下,通过让多个数据库键共享同一个对象来节约内存

最后,redis对象带有访问时间记录信息,该信息可以用于计算数据库键的空转时长,在服务器启用了maxmenory功能的情况下,空转时长较大的那些键可能会优先被服务器删除。

1、对象的类型与编码

redis使用对象来表示数据库中的键和值,每次当我们在redis 的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用作键值对的键,另一个对象用于键值对的值。

reids中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性如下

typedef struct redisObject{
//类型
unsigned type:4;
//编码unsigned encoding:4;
//指向底层实现数据结构的指针
void *ptr; …….
}robj

1.1、类型

对象的type属性记录了对象的类型,这个属性的值是下表中其中的一个。

对于redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以是字符串对象、列表对象、哈希对象、集合对象或者有序集合对象其中一种。所以我们执行type命令时,命令返回的结果为数据库键对应的值对象的类型,而不是键对象的类型。

2、编码和底层实现

对象ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。encoding属性记录了对象所使用的编码,也即是说这个对象使用了什么数据结构作为对象的底层实现,这个属性可以是下面列出的常量的其中之一。

每种类型的对象都至少使用了两种不同的编码,每种类型的对象可以使用的编码如下

2.1、字符串对象

字符串对象的编码可以使int,raw或者embstr。如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void *转换成long),并将字符串对象的编码设置为int。

举个例子,执行以下命令

set number 10086 object encoding number //”int”

如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节【每个版本对应的字节不同,最新版本是44字节】,那么字符串对象将使用一个简单动态字符串(sds)来保存这个字符串值,并将对象的编码设置为raw。

如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。

embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构来表示字符串对象,但raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中一次包含redisObject和sdshdr连个结构。如下图:

embstr编码的字符串对象在执行命令时,产生的效果和raw编码的字符串对象执行命令时产生的效果是相同的,但使用embstr编码的字符串对象来保存短字符串值有以下好处:

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

最后要说的是,可以用long double类型表示的浮点数在redis中也是作为字符串值来保存的。如果我们要保存一个浮点数到字符串对象里面,那么程序会先将这个浮点数转换成字符串值,然后再保存转换所得的字符串值。

2.1.1、编码的转化

int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象。redis没有为embstr编码字符串对象编写任何相应修改功能,所以embstr编码字符串对象实际上是只读的。当我们对embstr编码字符串修改时,先将对象编码从embstr转换成raw。

2.2、列表对象

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

ziplist编码的列表对象使用压缩列表作为底层实现,每个压缩列表节点(entry)保存了一个列表元素。下图就是ziplist编码的列表对象,红框内为存储的数据。

另一方面,linkedlist编码的列表对象使用双端链表作为底层实现,每个双端链表节点都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素,如下图

编码转换

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

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

(以上两个条件的上限值可以在配置文件中修改)

2.3、哈希对象

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

ziplist编码的哈希对象使用压缩列表作为底部实现,每当有新的键值对要加入到哈希对象时,程序会先保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾,因此:

  1. 保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点在后
  2. 先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向。

举个例子,执行如下命令

hset profile name “Tom” hset profile age 25 hset profile career “Programmer”

另一方面,hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存:

  1. 字典的每个键都是一个字符串对象,对象中保存了键值对的键
  2. 字典的每个值都是一个字符串对象,对象中保存了键值对的值

编码转换

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

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

不能满足这两个条件的哈希对象需要使用hashtable编码(这两个条件的上限值可以在redis配置中修改。)

2.4、集合对象

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

intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面。另一方面,hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为null。

编码转换

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

  1. 集合对象保存的所有元素都是整数值
  2. 集合对象保存的元素数量不超过512个

不能满足这两个条件的集合对象使用hashtable编码。

2.5、有序集合对象

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

ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个金爱在一起的压缩列表节点保存,第一个节点保存元素的成员,而第二个元素则保存元素的分值。

压缩列表内的集合元素按分值从小到大金星排序,分值较小的元素被防止在靠近表头的方向,而分值较大的元素责备防止在靠近表尾的方向,如下图。

skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表:

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

zset结构中的zsl跳跃表按分值从小到大保存所有集合元素,每个跳跃表节点都保存了一个集合元素:跳跃表节点的object属性保存了元素的成员,而跳跃表节点的score属性则保存了元素的分值。通过这个跳跃表,程序可以对有序集合进行范围性操作,比如zrank、zrange等命令就是基于跳跃表api来实现的。

除此之外,zset结构中的dict字典为有序集合创建了一个从成员到分值的映射,字典中的每个键值对都保存了一个集合元素:字典的键保存了元素的成员,而字典的值则保存了元素的分值。通过这个字典,程序可以用O(1)复杂度查找给定成员的分值,ZSCORE命令就是根据这一特性实现的,而很多其他有序集合命令都在实现的内部用到了这一特性。

有序集合每个元素的成员都是一个字符串对象,而每个元素的分值都是一个double类型的浮点数。值得一提的是,虽然zset结构同时使用跳跃表和字典来保存有序集合元素,但这两种数据结构都会通过指针来共享相同的成员和分值,所以同时使用跳跃表和字典来保存集合元素不会产生任何重复成员或分值,也不会因此而浪费额外的内存。

编码的转换

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

  1. 有序集合同时保持的元素数量小于128个
  2. 有序集合保存的所有元素成员的长度都小于64字节

参考

《Redis 的设计与实现(第二版)》

最后

可以关注个人公众号 【CodingCode】优先获取一手资料,同时也会不定时更新更多学习笔记。

Redis内存模型

Redis开发规范

Redis数据结构-对象相关推荐

  1. redis数据结构对象

    redis的数据结构对象包括 字符串 列表 哈希 集合 有序集合五种数据结构对象,由底层的8种数据结构组成这五种对象,每种对象的实现不同的数据都是不一样的. 结构 typedef struct red ...

  2. 【Redis-09】面试题之Redis数据结构与对象-RedisObject(下篇)

     承接上篇[Redis-08]面试题之Redis数据结构与对象-RedisObject(上篇) 8. type-字符串string 8.1 字符串的三种encoding编码(int + embstr ...

  3. 「Redis数据结构」哈希对象(Hash)

    「Redis数据结构」哈希对象(Hash) 文章目录 「Redis数据结构」哈希对象(Hash) 一.概述 二.编码 ZipList HashTable 三.编码转换 一.概述 Redis中hash对 ...

  4. 「Redis数据结构」集合对象(Set)

    「Redis数据结构」集合对象(Set) 文章目录 「Redis数据结构」集合对象(Set) 一.概述 二.结构 三.编码转换 四.小结 一.概述 Set是Redis中的单列集合,其特点为不保证有序性 ...

  5. 「Redis数据结构」字符串对象(String)

    「Redis数据结构」字符串对象String 文章目录 「Redis数据结构」字符串对象String 一.概述 二.编码分类 int embstr row 三.小结 四.参考 一.概述 字符串数据类型 ...

  6. Redis 数据结构-字典源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...

  7. 为了拿捏 Redis 数据结构,我画了 40 张图

    Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. 因此,这次我们就来好 ...

  8. 【带你重拾Redis】Redis数据结构及使用场景

    Redis数据结构 Redis有着非常丰富的数据结构,这些数据结构可以满足非常多的应用场景, 如果对这些数据结构有一个比较清晰的认知,使用Redis也会更加得心应手. Redis主要支持以下数据结构: ...

  9. redis 数据结构

    2019独角兽企业重金招聘Python工程师标准>>> redis对象 redis 数据结构 字符串对象  SDS(简单动态字符串) 列表对象  压缩列表(ziplist) 或 链表 ...

最新文章

  1. android重启软件用不了,应用程序重启而不是重启
  2. php--在类中声明常量的关键字
  3. ole db 访问接口 sqlncli 无法启动分布式事务_阿里终面:分布式事务原理
  4. 数据库-优化-通过慢查日志查询有问题的sql
  5. ASP.NET Core开发者成长路线图
  6. mysql数据表内容_MySQL数据表
  7. 4 种最令人讨厌的编程语言:Java、C++ 上榜
  8. Android存储-SharedPreferences
  9. paip.语义相关是否可在 哈米 的语义分析中应用
  10. 彻底理解SVD奇异值分解(singular value decomposition)
  11. 大数据基础概念思维导图
  12. 接口测试用例设计思路_学习接口测试,你需要知道这些!
  13. 转帖 美国 工程索引 收录中国科技论文的最新规定
  14. 计算机一级考试图片水印怎么加,图片水印怎么添加?一起来看看这几个方法
  15. linux 的常用命令---------第十阶段(虚拟机三种网络模式)
  16. 摄像头网络直播方案LiveGBS部署问题 使GB28181实现无插件web直播
  17. 离职,见人品,显格局
  18. arduino液晶显示屏与温湿度传感器连接
  19. TCP/IP之大明王朝邮差
  20. 非正式纳新题解(C语言)

热门文章

  1. [树状数组][哈希]JZOJ 3240 Seat
  2. gvim最简化设置,去掉工具栏和菜单栏
  3. hdu 5076 最小割灵活运用
  4. GROUP BY,WHERE,HAVING之间的差别和使用方法
  5. JS 判断一个字符串是否包含在一个数组中
  6. [置顶] J2EE (八) 策略模式+装饰模式+反射(java)
  7. Adroid新增硬件编解码
  8. 从FTP入侵到SQL
  9. 何时使用hadoop fs、hadoop dfs与hdfs dfs命令(转)
  10. JavaScript 演练(4). 回调函数