一、Redis对象结构
Redis中的每个对象都由一个redisObject结构表示:

typedef struct redisObject {
unsigned type;//类型
unsigned encoding;//编码
void *ptr;//指向底层实现数据结构的指针
int refcount;//引用计数
unsigned lru;//对象最后一次被程序访问的时间
}

1. type:Redis对象类型
redisObject的'type'属性记录了对象的类型:

type命令:返回键对应的值对象的类型。

2. encoding:Redis对象编码和底层实现
redisObject的'encoding'属性记录了对象所使用的编码(记录对象底层实现的数据结构):

每种类型的对象都至少使用了两种不同的编码:

object encoding命令:查看一个数据库键的值对象的编码。

  通过'encoding'属性来设定Redis对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,提升了Redis的灵活性和效率,因为Redis可以根据不同的使用场景来为一个对象设置不同的编码, 从而优化对象在某一场景下的效率。

  Redis服务器执行一个命令时,首先通过'type'属性进行类型检查,判断对象类型是否支持当前命令,然后再根据'encoding'属性判断对象的编码方式,选择正确的命令实现代码来执行命令。

3. refcount:Redis对象引用计数

  Redis对象通过其'refcount'属性实现对象内存回收与对象共享。当Redis需要创建一个键值对的时候,如果已经存在一个与需要创建的值对象完全相同的值对象,Redis不会创建一个新的值对象,而是将键对象的值指针指向这个现有的完全相同的值对象,同时将这个值对象的'refcount'属性值加一,从而实现对象共享,节约内存;当一个键对象不再指向这个值对象的时候,则值对象的'refcount'属性值减一,当'refcount'属性值为0的时候,则将对象释放,将内存回收。

  目前,Redis会在初始化服务器时,创建一万个字符串对象,这些对象包含了从0到9999的所有整数值,当服务器需要用到值为0到9999的字符串对象时,服务器就会使用这些共享对象,而不是新创建对象。创建共享字符串对象的数量可以通过修改redis.h/REDIS_SHARED_INTEGERS常量来修改。

  当服务器考虑将一个共享对象设置为键的值对象时,程序会先验证给定的共享对象和键想创建的目标对象是否完全相同,共享对象保存的值越复杂,验证所需的复杂度就越高,消耗的CPU时间也会越多,所以Redis只对包含整数值的字符串对象进行共享,其验证复杂度为O(1)。
引用计数查看命令:object refcount

4. lru:Redis对象最后一次被程序访问的时间
对象的空转时长 = 当前时间 - 对象的'lru'属性值
查看对象空转时长的命令:object idletime(这个命令不会改变对象的'lru'属性值)
  如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存(可以参考配置文件的maxmemory选项和maxmemory-policy选项)。

二、字符串对象
1. 字符串对象根据其值类型的不同,使用不同的编码方式,对应关系如下:
(1)可以用long类型表示的整数值:int
(2)小于等于39字节的字符串值:embstr(底层结构为embstr编码的SDS)
(3)大于39字节的字符串值:raw(底层结构为SDS)
注:浮点数在Redis中也是作为字符串值来保存的,只是在有需要的时候会将其转化为浮点数值,执行某些操作之后,存入Redis时又转换为字符串值。
2. 关于embstr编码
  embstr编码是专门用于保存短字符串的一种优化编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构来表示字符串对象,但raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构。
使用embstr编码的字符串对象来保存短字符串值的好处:
(1)embst 编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次。
(2)释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的字符串对象需要调用两次内存释放函数。
(3)因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面,所以这种编码的字符串对象比起raw编码的字符串对象能够更好地利用缓存带来的优势。
3. 三种编码的字符串对象结构
(1)int编码的字符串对象结构示例:

(2)raw编码的字符串对象结构示例:

(3)embstr编码的字符串对象结构示例:

4. 编码的转换
  int编码的字符串对象和embstr编码的字符串对象在条件满足的情况下,会被转换为raw编码的字符串对象。
  另外,因为Redis没有为embstr编码的字符串对象编写任何相应的修改程序(只有int编码的字符串对象和raw编码的字符串对象有这些程序),所以embstr编码的字符串对象实际上是只读的:当我们对embstr编码的字符串对象执行任何修改命令时,程序会先将对象的编码从embstr转换成raw,然后再执行修改命令;因为这个原因,embstr编码的字符串对象在执行修改命令之后,总会变成一个raw编码的字符串对象。

三、列表对象
1. 列表对象的编码可以是ziplist(底层结构为压缩列表)或者linkedlist(底层结构为双端链表)。
2. 列表对象结构
(1)使用ziplist编码的列表对象结构示例:

(2)使用linkedlist编码的列表对象结构示例:

其中双端链表中又嵌套了多个字符串对象,这里的‘StringObject’只是简化表示。

3. 编码转换
  当列表对象可以同时满足以下两个条件时, 列表对象使用ziplist编码:
(1)列表对象保存的所有字符串元素的长度都小于64字节(可由参数list-max-ziplist-value配置);
(2)列表对象保存的元素数量小于512个(可由参数list-max-ziplist-entries配置);
不能同时满足这两个条件的列表对象需要使用linkedlist编码。

四、哈希对象
1. 哈希对象的编码可以是ziplist(底层结构为压缩列表)或hashtable(底层结构为字典)。
2. 哈希对象结构
(1)使用ziplist编码的哈希对象结构示例:

其中压缩列表结构示例如下:

(2)使用hashtable编码的哈希对象结构示例:

3. 编码转换
  当哈希对象可以同时满足以下两个条件时, 哈希对象使用ziplist编码:
(1)哈希对象保存的所有键值对的键和值的字符串长度都小于64字节(可由参数hash-max-ziplist-value配置);
(2)哈希对象保存的键值对数量小于512个(可由参数hash-max-ziplist-entries配置);
不能同时满足这两个条件的哈希对象需要使用hashtable编码。

五、集合对象
1. 集合对象的编码可以是intset(底层结构是整数集合)或hashtable(底层结构是字典)。
2. 集合对象结构
(1)使用intset编码的集合对象结构示例:

(2)使用hashtable编码的集合对象结构示例:

(使用字典键值对中的键来存储集合元素,而值都被设置为NULL)
3. 编码转换
  当集合对象可以同时满足以下两个条件时,对象使用intset编码:
(1)集合对象保存的所有元素都是整数值;
(2)集合对象保存的元素数量不超过512个(可由参数set-max-intset-entries配置);
不能满足这两个条件的集合对象需要使用hashtable编码。

六、有序集合对象
1. 有序集合的编码可以是ziplist(底层结构为压缩列表)或skiplist(底层结构为跳跃表+字典)。
2. 有序集合对象结构
(1)使用ziplist编码的有序集合对象结构示例:

其中压缩列表结构示例如下:

(2)使用skiplist编码的有序集合对象
  使用skiplist编码的有序集合对象同时使用一个字典和一个跳跃表来实现。
结构定义:

结构示例:

其中zset结构示例如下:

  理论上有序集合完全可以单独使用字典或跳跃表其中一种数据结构来实现,但是性能上比同时使用这两种数据结构都会有所下降。使用跳跃表可以提高对有序集合进行范围型操作的效率,而使用字典则可以快速找到指定成员的分值,只使用其中一种数据结构会丢失另一种数据结构在对应操作上带来的效率。另外,有序集合中的字典和跳跃表会通过指针共享相同元素的成员和分值,并不会造成任何数据重复,也不会浪费额外内存。
3. 编码转换
  当有序集合对象可以同时满足以下两个条件时, 对象使用ziplist编码:
(1)有序集合保存的元素数量小于128个(可由参数zset-max-ziplist-entries配置);
(2)有序集合保存的所有元素成员的长度都小于64字节(可由参数zset-max-ziplist-value配置);
不能满足以上两个条件的有序集合对象将使用skiplist编码。

Redis对象的设计与实现的更多相关文章

  1. Redis集群设计原理

    ---恢复内容开始--- Redis集群设计包括2部分:哈希Slot和节点主从,本篇博文通过3张图来搞明白Redis的集群设计. 节点主从: 主从设计不算什么新鲜玩意,在数据库中我们也经常用主从来做读 ...

  2. 面试官:你了解过Redis对象底层实现吗

    上一章我们讲了Redis的底层数据结构,不了解的人可能会有疑问:这个和平时用的五大对象有啥关系呢?这一章我们就主要解释他们所建立的联系. 看这个文件之前,如果对ziplist.skiplist.int ...

  3. Redis之对象篇——Redis对象系统简介

    Redis之对象篇--Redis对象系统简介 前言     之前几篇文章,简单介绍 Redis用到的所有主要数据结构,简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合.跳跃表. 图解Red ...

  4. Redis对象——字符串

    文章导航-readme 前言     上一篇文章Redis之对象篇--Redis对象系统简介简单介绍了Redis的对象系统.Redis使用对象来表示数据库中的键和值每个对象都由一个redisObjec ...

  5. Redis键值设计(转载)

    参考资料:https://blog.csdn.net/iloveyin/article/details/7105181 丰富的数据结构使得redis的设计非常的有趣.不像关系型数据库那样,DEV和DB ...

  6. 【集群】Redis集群设计原理

    Redis集群设计包括2部分:哈希Slot和节点主从 节点主从: 主从设计不算什么新鲜玩意,在数据库中我们也经常用主从来做读写分离,直接上图: 图上能看得到的信息: 1, 只有1个Master,可以有 ...

  7. 三张图秒懂Redis集群设计原理

    转载Redis Cluster原理 转载https://blog.csdn.net/yejingtao703/article/details/78484151 redis集群部署方式: 单机 主从 r ...

  8. 【redis源码阅读】redis对象

    结构定义 在redis中,对象的数据结构定义如下: ​typedef struct redisObject { ​unsigned type:4; ​unsgined encoding:4; ​uns ...

  9. Redis对象类型

    Redis对象类型 Redis基于基础的数据结构创建的对象: 字符串对象. 列表对象. 哈希对象. 集合对象 有序集合对象. 对象回收:Redis对象系统实现了基于引用计数技术的内存回收机制,当程序不 ...

随机推荐

  1. 2、Task 使用 ContinueWith 而不要使用 Wait

    1.线程自旋:在阻塞线程的时候为了等待解锁(访问临界资源)(Sleep). 2.上下文切换:将处理器当前线程的状态保存到操作系统内部的线程对象中,然后再挑出一个就绪的线程,把上下文信息传递给处理器,然 ...

  2. No-11.变量进阶

    变量进阶 目标 变量的引用 可变和不可变类型 局部变量和全局变量 01. 变量的引用 变量 和 数据 都是保存在 内存 中的 在 Python 中 函数 的 参数传递 以及 返回值 都是靠 引用 传递 ...

  3. HTML网页的浏览器标题栏显示小图标的方法

    HTML网页的浏览器标题栏显示小图标的方法   就像这种效果,方法其实很简单,就是 在head头部里写: <link rel='icon' href='pic.ico ' type='image ...

  4. CF-1096C Polygon for the Angle

    CF-1096C Polygon for the Angle https://codeforces.com/contest/1096/problem/C 题意:给一个角度ang(1<=ang&l ...

  5. CF895E Eyes Closed (期望)

    题目链接 利用期望的线性性质: \(E(sum) = E(x_l) + E(x_{l+1})+ E(x_{l+2}) +.. E(x_r)\) 然后就考虑对于交换时两个区间元素的改动. 假设这两个区间 ...

  6. HDU-1241-油藏

    这题一道深搜的简单题目,其实题目的思路就只是向八个方向搜索,然后把整个油田遍历一遍即可. #include <cstdio> #include <cstring> int ma ...

  7. [LUOGU] 1892 团伙

    题目描述 1920年的芝加哥,出现了一群强盗.如果两个强盗遇上了,那么他们要么是朋友,要么是敌人.而且有一点是肯定的,就是: 我朋友的朋友是我的朋友: 我敌人的敌人也是我的朋友. 两个强盗是同一团伙的 ...

  8. IntelliJ IDEA 中自定义模板代码的缩写

    方法一:新建 Live Template step1. 点击 File – Setting step2.选择 Live Template,点击右侧的+号,选择 Template Group step3 ...

  9. Web框架之Django_10 重要组件(Auth模块)

    一.auth模块介绍 Auth模块是Django自带的用户认证模块: 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括用户注册.用户登录.用户认证.注销.修改密码等 ...

  10. Ubuntu 16.04如何使用无线网卡上网

    我使用的无线网卡卡托型号是华为E8372h,网卡是普通电信卡(既可以打电话也可以上网). 按照“芯片朝上.缺口朝外.用最大卡”的方法将网卡装入卡托后,紧接着便将卡托插入笔记本对应的USB接口中. 在这 ...