Redis底层数据结构

前言

  Redis中共有五种常用的数据类型,分别是stringlistsethash以及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;

  typeencodingptrredisObject最重要的三个属性。

type

  type字段表示对象的类型,占4个bit,它记录了对象所保存的值的类型。我们可以通过客户端使用type命令来查看对象的类型,如下所示:

  type的常见取值如下:

/*
* 对象类型
*/
#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类型,其底层实现就有intembstrraw三种编码方式。使用 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-lruvolatile-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

image.png

参考

深入学习Redis(1):Redis内存模型 

图解redis五种数据结构底层实现

redis 学习笔记

posted @   Reecelin  阅读(787)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· 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代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示