当前位置:网站首页>一文深入底层分析Redis对象结构
一文深入底层分析Redis对象结构
2022-06-26 06:57:00 【Java爱好狂.】
我们在前文已经阐述了Redis 5种基础数据类型详解,分别是字符串(string)、列表(list)、哈希(hash)、集合(set)、有序集合(zset),以及5.0版本中Redis Stream结构详解;那么这些基础类型的底层是如何实现的呢?Redis的每种对象其实都由对象结构(redisObject) 与 对应编码的数据结构组合而成, 本文主要介绍对象结构(redisObject) 部分。
引入:从哪里开始学习底层?
我在整理Redis底层设计时,发现网上其实是有很多资料的,但是缺少一种自上而下的承接。这里我将收集很多网上的资料并重新组织,来帮助你更好地理解Redis底层设计。@pdai
那么这些基础类型的底层是如何实现的呢?带着这个问题我们来着手理解底层设计,首先看下图:

它反映了Redis的每种对象其实都由对象结构(redisObject) 与 对应编码的数据结构组合而成,而每种对象类型对应若干编码方式,不同的编码方式所对应的底层数据结构是不同的。
所以,我们需要从几个个角度来着手底层研究:
- 对象设计机制: 对象结构(redisObject)
- 编码类型和底层数据结构: 对应编码的数据结构
为什么Redis会设计redisObject对象
为什么Redis会设计redisObject对象?
在redis的命令中,用于对键进行处理的命令占了很大一部分,而对于键所保存的值的类型(键的类型),键能执行的命令又各不相同。如: LPUSH 和 LLEN 只能用于列表键, 而 SADD 和 SRANDMEMBER 只能用于集合键, 等等; 另外一些命令, 比如 DEL、 TTL 和 TYPE, 可以用于任何类型的键;但是要正确实现这些命令, 必须为不同类型的键设置不同的处理方式: 比如说, 删除一个列表键和删除一个字符串键的操作过程就不太一样。
以上的描述说明, Redis 必须让每个键都带有类型信息, 使得程序可以检查键的类型, 并为它选择合适的处理方式.
比如说, 集合类型就可以由字典和整数集合两种不同的数据结构实现, 但是, 当用户执行 ZADD 命令时, 他/她应该不必关心集合使用的是什么编码, 只要 Redis 能按照 ZADD 命令的指示, 将新元素添加到集合就可以了。
这说明, 操作数据类型的命令除了要对键的类型进行检查之外, 还需要根据数据类型的不同编码进行多态处理.
为了解决以上问题, Redis 构建了自己的类型系统, 这个系统的主要功能包括:
- redisObject 对象.
- 基于 redisObject 对象的类型检查.
- 基于 redisObject 对象的显式多态函数.
- 对 redisObject 进行分配、共享和销毁的机制.
redisObject数据结构
redisObject 是 Redis 类型系统的核心, 数据库中的每个键、值, 以及 Redis 本身处理的参数, 都表示为这种数据类型.
/*
* Redis 对象
*/
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码方式
unsigned encoding:4;
// LRU - 24位, 记录最末一次访问时间(相对于lru_clock); 或者 LFU(最少使用的数据:8位频率,16位访问时间)
unsigned lru:LRU_BITS; // LRU_BITS: 24
// 引用计数
int refcount;
// 指向底层数据结构实例
void *ptr;
} robj; 下图对应上面的结构

其中type、encoding和ptr是最重要的三个属性。
- 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 /* Raw representation */
#define OBJ_ENCODING_INT 1 /* Encoded as integer */
#define OBJ_ENCODING_HT 2 /* Encoded as hash table */
#define OBJ_ENCODING_ZIPMAP 3 /* 注意:版本2.6后不再使用. */
#define OBJ_ENCODING_LINKEDLIST 4 /* 注意:不再使用了,旧版本2.x中String的底层之一. */
#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
#define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */- ptr是一个指针,指向实际保存值的数据结构,这个数据结构由type和encoding属性决定。举个例子, 如果一个redisObject 的type 属性为OBJ_LIST , encoding 属性为OBJ_ENCODING_QUICKLIST ,那么这个对象就是一个Redis 列表(List),它的值保存在一个QuickList的数据结构内,而ptr 指针就指向quicklist的对象;
下图展示了redisObject 、Redis 所有数据类型、Redis 所有编码方式以及底层数据结构之间的关系(pdai:从6.0版本中梳理而来):

注意:OBJ_ENCODING_ZIPMAP没有出现在图中,因为在redis2.6开始,它不再是任何数据类型的底层结构(虽然还有zipmap.c的代码); OBJ_ENCODING_LINKEDLIST也不支持了,相关代码也删除了。
- lru属性: 记录了对象最后一次被命令程序访问的时间
空转时长:当前时间减去键的值对象的lru时间,就是该键的空转时长。Object idletime命令可以打印出给定键的空转时长
如果服务器打开了maxmemory选项,并且服务器用于回收内存的算法为volatile-lru或者allkeys-lru,那么当服务器占用的内存数超过了maxmemory选项所设置的上限值时,空转时长较高的那部分键会优先被服务器释放,从而回收内存。
命令的类型检查和多态
那么Redis是如何处理一条命令的呢?
当执行一个处理数据类型命令的时候,redis执行以下步骤
- 根据给定的key,在数据库字典中查找和它相对应的redisObject,如果没找到,就返回NULL;
- 检查redisObject的type属性和执行命令所需的类型是否相符,如果不相符,返回类型错误;
- 根据redisObject的encoding属性所指定的编码,选择合适的操作函数来处理底层的数据结构;
- 返回数据结构的操作结果作为命令的返回值。
比如现在执行LPOP命令:

对象共享
redis一般会把一些常见的值放到一个共享对象中,这样可使程序避免了重复分配的麻烦,也节约了一些CPU时间。
redis预分配的值对象如下:
- 各种命令的返回值,比如成功时返回的OK,错误时返回的ERROR,命令入队失误时返回的QUEUE,等等
- 包括0 在内,小于REDIS_SHARED_INTEGERS的所有整数(REDIS_SHARED_INTEGERS的默认值是10000)

注意:共享对象只能被字典和双向链表这类能带有指针的数据结构使用。像整数集合和压缩列表这些只能保存字符串、整数等自勉之的内存数据结构
为什么redis不共享列表对象、哈希对象、集合对象、有序集合对象,只共享字符串对象?
- 列表对象、哈希对象、集合对象、有序集合对象,本身可以包含字符串对象,复杂度较高。
- 如果共享对象是保存字符串对象,那么验证操作的复杂度为O(1)
- 如果共享对象是保存字符串值的字符串对象,那么验证操作的复杂度为O(N)
- 如果共享对象是包含多个值的对象,其中值本身又是字符串对象,即其它对象中嵌套了字符串对象,比如列表对象、哈希对象,那么验证操作的复杂度将会是O(N的平方)
如果对复杂度较高的对象创建共享对象,需要消耗很大的CPU,用这种消耗去换取内存空间,是不合适的
引用计数以及对象的消毁
redisObject中有refcount属性,是对象的引用计数,显然计数0那么就是可以回收。
- 每个redisObject结构都带有一个refcount属性,指示这个对象被引用了多少次;
- 当新创建一个对象时,它的refcount属性被设置为1;
- 当对一个对象进行共享时,redis将这个对象的refcount加一;
- 当使用完一个对象后,或者消除对一个对象的引用之后,程序将对象的refcount减一;
- 当对象的refcount降至0 时,这个RedisObject结构,以及它引用的数据结构的内存都会被释放。
小结
- redis使用自己实现的对象机制(redisObject)来实现类型判断、命令多态和基于引用次数的垃圾回收;
- redis会预分配一些常用的数据对象,并通过共享这些对象来减少内存占用,和避免频繁的为小对象分配内存。
边栏推荐
- 一芯实现喷雾|WS2812驱动|按键触摸|LED显示|语音播报芯片等功能,简化加湿器产品设计
- Web technology sharing | webrtc recording video stream
- Invalid problem of self defined map used by Gaode map
- Simple use of enum type in TS
- Dpdk - tcp/udp protocol stack server implementation (I)
- 同步通信和异步通信的区别以及优缺点
- JS download pictures
- Bugku练习题---MISC---富强民主
- Research Report on market supply and demand and strategy of Chinese amyl cinnamaldehyde (ACA) industry
- Format one insert per line using mysqldump- Using mysqldump to format one insert per line?
猜你喜欢
![[digital signal processing] basic sequence (unit step sequence | relationship between unit step sequence and unit pulse sequence | rectangular sequence | relationship between rectangular sequence and](/img/0c/3aeb7f93576181bbc46d2d1a1aa0e7.jpg)
[digital signal processing] basic sequence (unit step sequence | relationship between unit step sequence and unit pulse sequence | rectangular sequence | relationship between rectangular sequence and

Kotlin Compose 状态恢复 rememberSaveable 与 remember

Numpy learning challenge level 4 - numpy array attribute
![[image segmentation] blood vessel extraction from retinal fundus images based on maximum principal curvature with matlab code](/img/10/3cef095ae5834bbbe51ef16b5c9f78.png)
[image segmentation] blood vessel extraction from retinal fundus images based on maximum principal curvature with matlab code

【元胞自动机】基于元胞自动机实现高速公路收费站交通流问题附matlab代码

浅析一道经典题

Go language learning notes 1.1

LabVIEW Arduino tcp/ip remote smart home system (project part-5)

What is data mining?

Analyse d'un problème classique
随机推荐
网络io,磁盘io
面试官:测试计划和测试方案有什么区别?
Vulnerability discovery - API interface service vulnerability probe type utilization and repair
MySQL
Operation mode and investment planning report of China's financial warehousing industry during the "14th five year plan" period 2022-2027
If you meet a female driver who drives didi as an amateur, you can earn 500 yuan a day!
China's Ni MH battery industry development overview analysis and investment trend forecast report 2022 Edition
Research Report on market supply and demand and strategy of Chinese amyl cinnamaldehyde (ACA) industry
MATLAB线性规划模型学习笔记
js-下载图片
【特征提取】基于稀疏PCA实现目标识别信息特征选择附matlab源码
MySQL delete in without index
LabVIEW arduino TCP / IP Remote Intelligent Home System (Project section - 5)
Decompile Android applications, interview Android
Marketing skills: compared with the advantages of the product, it is more effective to show the use effect to customers
SQL Basics
Fmt Must the result of println (true) be true?
寶塔服務器搭建及數據庫遠程連接
Pagoda server setup and database remote connection
Professional course - Code question record