Redis对象的设计与实现

一、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编码。

原文地址:https://www.cnblogs.com/wujuntian/p/9201613.html

时间: 2024-08-30 11:08:07

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

Python快速学习10: 循环的对象及设计 (生活的规律)

前言 系列文章:[传送门] 生活逐渐规律,按时睡觉.今天写博客,明天补时间看会书.慢慢的时间很珍惜 我很喜欢! 时钟就像个循环体,我们将它融入生活. 正文 循环对象的并不是随着Python的诞生就存在的,但它的发展迅速,特别是Python 3x的时代,循环对象正在成为循环的标准形式. 灵活的循环方式 (我晚饭后爱上了萨克斯,因为这是生活的一部分.属于我的特殊循环对象,它的按键就像循环方式,然后出来一首美丽的歌曲) 我的萨克斯偶像 循环对象 循环对象是这样一个对象,它包含有一个next()方法(_

Redis的Client设计

本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/25432823 Redis的client设计如下: /* With multiplexing we need to take per-clinet state. * Clients are taken in a liked list. */ typedef struct redisClient { int fd; redisDb *db; int di

服务返回对象的设计

服务返回对象的设计 服务层的定义 本文中,服务层指暴露给其他进程调用的外部接口的集合. 英文单词含义 service:服务response:服务返回对象request:服务请求对象 服务调用模型 在同一个线程内,现代计算机语言一般会提供异常捕获(try-catch)机制,模型如图1 . 在这个模型中,response只需包含业务数据, 异常情况用异常捕获机制处理. ----------------- request / / response ------------> / service / -

为什么C/C++程序员都要阅读Redis源码之:Redis学习事件驱动设计

为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你源码编译安装过Nginx/Grpc/Thrift/Boost等开源产品,你会发现有很多依赖,而依赖本身又有依赖,十分痛苦.通常半天一天就耗进去了.由衷地羡慕 npm/maven/pip/composer/...这些包管理器.而Redis则给人惊喜,一行make了此残生. 除了安装过程简洁,代码也十分简洁.

Redis初识、设计思想与一些学习资源推荐

一.Redis简介 1.什么是Redis Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的API.从2010 年3 月15 日起,Redis 的开发工作由VMware 主持. Redis 是一个Key-Value 存储系统.和Memcached 类似,它支持存储的value 类型相对更多, 包括string(字符串).hash(散列).list(链表).set(集合)和zset(有序集合).这些数据类型支持p

投稿007期|令人震惊到发指的PyObject对象代码设计之美

前言 最近在重温经典漫画<SlamDunk>的全国大赛篇,其中的一个情形可以很好的诠释虎躯一震这个状态--当樱木看到流川枫一次高难度投篮时内心的感受:"经过两万次射球练习后,樱木首次明白到流川枫这一球是相当厉害的,那正是他在两万次射球练习之中,经常在他脑海中出现的理想射球姿势". 言归正传,其实对大多数程序开发人员来说,以上这个场景的感慨状态有时候也出现在我们看到经典代码的时候.最近正在思考关于Python语言的源生设计机制,有个问题不知道大家是否也有思考过:我们知道Pyt

PHP对象与设计

在程序中包含了类的声明,引用,并不能就认为是面向对象.面向对象是一种开发理念.面向对象和面向过程的核心区别是如何分配指责.过程式编程表现为一系列命令和方法的连续调用,代码中含有很多的条件判断,关联度高.这种自顶向下的控制方式导致了重复和相互依赖的代码遍布于整个项目.面向对象,则将职责移交到专门的对象中,减少代码之间的相互联系. 过程式编程有一下缺点:1.任何项目,需求是不断变化的,过程式编程,导致代码越来越繁杂,维护性差2.代码冗余3.代码阅读,维护,扩展都变得异常艰难. 面向对象编程,一般应该

Redis+PHP秒杀设计

讲师介绍: 稳定性建设: 如何做到服务9999的稳定性即每年不可用时间不到1min 高性能系统: 滴滴运营广告系统即并发量很大 社招面试: 秒杀系统的设计与实现,其本质高并发高可用,这两方面的技术可直接决定公司技术水平,提现个人技术实力 概要: 基本知识和原理 减而治之 cdn原理: 减少读的压力,如订单详情页下发到不同地方的cdn节点,访问加速,回源减少 Nginx限流: 请求到达服务端(即接入层)如何做过载保护 异步队列概念: 如通过异步方式创建订单 分而治之 Nginx负载均衡: 流量到达

redis模型(2):redis对象

一.对象 Redis使用对象来表示数据库中的键和值,每次当我们在redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个键对象,一个值对象. Redis中的每个对象都由一个redisObject结构表示. typedef struct redisObject { // 对象类型 unsigned type:4; // 编码 对象使用的底层数据结构 unsigned encoding:4; // 指向对象的底层实现数据结构 void *ptr; // 对象最后一次被访问的时间 unsi