Redis底层数据结构
前言
Redis
中共有五种常用的数据类型,分别是string
、list
、set
、hash
以及zset
。但是其底层数据结构是啥样的以及是如何实现的呢?今天我们就深入了解一下Redis
中的底层数据结构。
redisObject
当我们使用Redis
中各种类型来存储数据时,无论是哪种类型,Redis
中都不是直接存储的,而是存储在一个叫做redisObject
的数据结构中。不同的版本redisObject
结构可能不同,但都具有如下的结构:
typedef struct redisObject {
// 类型 string list set hash zset等 4bit
unsigned type:4;
// 编码方式 4bit
unsigned encoding:4;
// LRU 时间 24bit
unsigned lru:LRU_BITS;
// 引用计数 4byte
int refcount;
// 指向对象的指针 8byte
void *ptr;
} robj;
type
、encoding
和 ptr
是redisObject
最重要的三个属性。
type
type
字段表示对象的类型,占4个bit
,它记录了对象所保存的值的类型。我们可以通过客户端使用type
命令来查看对象的类型,如下所示:
typ
e的常见取值如下:
/*
* 对象类型
*/
#define REDIS_STRING 0 // 字符串
#define REDIS_LIST 1 // 列表
#define REDIS_SET 2 // 集合
#define REDIS_ZSET 3 // 有序集
#define REDIS_HASH 4 // 哈希表
encoding
encoding
表示对象的内部编码,占4个bit
,其实也就是该对象底层到底使用的哪种数据结构来存储。
由于Redis
对内存进行了极致的利用,为了节省空间,Redis
底层会根据对象大小使用不同的数据结构来存储。通过encoding
属性,Redis
可以根据不同的使用场景来为对象设置不同的编码,大大提高了Redis
的灵活性和效率。例如,常见的string
类型,其底层实现就有int
、embstr
和raw
三种编码方式。使用 object encoding key 命令可以查看对象的编码方式,如下所示:
5种对象类型对应的编码方式将在后面细说,这里先留下一个印象。而在redisObject
中,encoding
的取值有以下这些:
/*
* 对象编码
*/
#define REDIS_ENCODING_RAW 0 // 编码为字符串
#define REDIS_ENCODING_INT 1 // 编码为整数
#define REDIS_ENCODING_HT 2 // 编码为哈希表
#define REDIS_ENCODING_ZIPMAP 3 // 编码为 zipmap(2.6 后不再使用)
#define REDIS_ENCODING_LINKEDLIST 4 // 编码为双端链表
#define REDIS_ENCODING_ZIPLIST 5 // 编码为压缩列表
#define REDIS_ENCODING_INTSET 6 // 编码为整数集合
#define REDIS_ENCODING_SKIPLIST 7 // 编码为跳跃表
ptr
ptr
指针指向具体的数据,如 set test hello
命令,ptr
指向的就是存储hello
的字符串。
lru
lru
记录的是对象最后一次被命令程序访问的时间,占用的bit
因版本不同而不同,2.6版本22 bit
、4.0版本为24 bit
。
通过对比lru
时间与系统当前时间,可以计算某个对象的在Redis
中存在了多长时间,使用object idletime 即可看到(注:单位是秒),如下所示:
object idletime
命令的一个特殊之处在于它不改变对象的lru
值。另外,说到lru
,是不是感觉特别熟悉。没错,Redis
的数据淘汰策略中,就有allkeys-lru
和volatile-lru
两种,当redis.conf
中设置了maxmemory
选项时,若是Redis
内存占用超过maxmemory
指定的值时,Redis
会优先选择生存时间较长的对象进行释放,从而达到回收内存的目的。
refcount
refcount
记录的是该对象被其他引用的次数,类型为整型。refcount
的作用主要在于记录对象的引用计数及内存回收。当创建对象时,refcount
初始化为1;当有新程序使用该对象时,refcount++
;当对象不再被一个程序使用时,refcount--
;当refcount
变为0时,即代表对象可以被回收,其占用的内存将会被释放。
Redis
中被多次使用的对象,即refcount>1
的被称为共享对象。Redis为了节省内存,当有一些重复对象出现时,新的程序不会创建新的对象,而是直接使用重复对象,然后使refcount++
。需要注意的是,目前共享对象仅仅支持整数值的字符串对象。
之所以只支持整数型共享对象,是出于对内存和CPU
的平衡:共享对象虽然会降低内存消耗,但是判断两个对象是否相等却需要消耗额外的时间。对于整数值,判断操作复杂度为O(1)
;对于普通字符串,判断复杂度为O(n)
;而对于哈希、列表、集合和有序集合,判断的复杂度为O(n^2)
。
就目前的实现来说,Redis
服务器在初始化时,会创建10000个字符串对象,值分别是**09999**之间的整数值;当`Redis`需要使用值为09999的字符串对象时,可以直接使用这些共享对象。10000这个数字可以通过调整redis.conf
文件中的OBJ_SHARED_INTEGERS
参数来修改。
共享对象的引用次数可以通过 object refcount 命令查看,如下图所示:
命令执行的结果页佐证了只有0~9999之间的整数会作为共享对象。
总结
综上所述,redisObject
的结构与对象类型、编码、内存回收、共享对象都有关系,一个redisObject
的大小约为16字节:4 bit
+ 4 bit
+ 24 bit
+ 4 byte
+ 8 byte
= 16 byte
。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南