Redis7 底层数据结构解析

当我们使用 SET user:1001 "Alice"HSET product:123 name "Phone" price 999 时,Redis 内部究竟发生了什么?数据是如何被存储、查找和修改的?为什么 Redis 能如此之快?

答案就藏在其精心设计的底层数据结构 之中。Redis 并非简单地为每种数据类型(String, Hash, List 等)绑定一种固定的数据结构,而是采用了一种自适应、多态的策略。它会根据数据的规模、类型和内容,动态选择最合适的内部编码(encoding),以在内存占用和 CPU 性能之间取得最佳平衡。

本文将带你深入 Redis 7 的底层世界,逐一解析其核心数据类型的内部实现。

一、统一的对象模型:redisObject

在 Redis 内部,所有的值(value)都被封装在一个名为 redisObject 的结构体中:

c

编辑

复制代码
1structredisObject{2unsigned type:4;// 对象的上层类型 (string, list, hash...)3unsigned encoding:4;// 对象的底层编码方式4int refcount;// 引用计数5void*ptr;// 指向底层数据结构的指针6};
  • type 字段决定了我们看到的数据类型(如 string, hash)。
  • encoding 字段则揭示了其真正的底层存储格式(如 embstr, hashtable, quicklist)。
  • ptr 指针指向实际存储数据的内存区域。

正是这种设计,使得 Redis 能够为同一种上层类型提供多种底层实现。

二、String 类型的三种面孔

String 是最基础的类型,但其底层却有三种不同的编码:

  1. **int**:当字符串内容是一个可以表示为 long 类型的整数时,Redis 会直接将数值存储在 ptr 指针中(利用了指针的低几位来区分),省去了额外的内存分配。
  2. **embstr (Embedded String)**:对于长度小于等于 44 字节 的短字符串,Redis 会将 redisObject 和其底层的 SDS(Simple Dynamic String)分配在同一块连续的内存中。这减少了内存分配次数,并提高了缓存局部性,读取速度更快。
  3. **raw**:对于长度大于 44 字节的长字符串,redisObject 和 SDS 会分开分配内存,ptr 指向 SDS 结构。

SDS (简单动态字符串) 是 Redis 自己实现的字符串结构,它通过记录字符串长度 (len) 避免了 C 字符串遍历计算长度的 O(N) 开销,并且是二进制安全的。

三、Hash, List, Set, ZSet 的底层演进:告别 ziplist,拥抱 listpack

这是 Redis 7 相比 Redis 6 ​最重大的变化 ​。过去广泛使用的 ziplist(压缩列表)因其连锁更新 问题(一个节点的扩容可能引发后续所有节点的连锁调整)而被性能更优、结构更稳定的 listpack 所取代。

Hash (哈希)

  • 小对象 :当 Hash 中的键值对数量较少(默认 < 512)且每个值的长度较短(默认 < 64字节)时,底层使用 listpack 存储。listpack 将键和值紧凑地排列在一起,非常节省内存。
  • 大对象 :一旦超出阈值,Redis 会将其转换为 **hashtable**(哈希表),以保证 O(1) 的查询性能。注意:这个转换是单向的,一旦升级为 hashtable,就不会再降级回 listpack。

List (列表)

  • 小列表 :对于元素较少的列表,底层直接使用 **listpack**。
  • 大列表 :对于元素较多的列表,Redis 使用 **quicklist​。quicklist 本质上是一个​ 双向链表​,但它的每个节点(quicklistNode)不再指向单个元素,而是指向一个 ​ listpack**。这种设计结合了链表的灵活性(O(1) 头尾插入/删除)和 listpack 的内存紧凑性,是工程上的完美折中。

Set (集合)

  • 整数集合 :如果 Set 中的所有元素都是整数,且数量不多(默认 < 512),则使用 **intset**(整数集合)存储,这是一个有序的整数数组,支持二分查找。
  • 普通集合 :对于包含字符串或数量较多的集合,则使用 hashtable 来保证 O(1) 的查找、插入和删除性能。

ZSet (有序集合)

  • 小有序集 :当元素数量较少(默认 < 128)且成员值较短(默认 < 64字节)时,使用 listpack 存储。成员和分数(score)会被打包在一起。
  • 大有序集 :超出阈值后,会转换为 **skiplist**(跳跃表)+ hashtable 的组合。skiplist 用于维护元素的有序性(支持 O(log N) 的范围查询),而 hashtable 则用于提供 O(1) 的单点查询(通过 member 查 score)。

四、总结:Redis 7 底层数据结构全景图

表格

上层数据类型 Redis 7 底层编码 (encoding)
String int,embstr,raw
Hash listpack,hashtable
List listpack,quicklist(内含 listpack)
Set intset,listpack,hashtable
ZSet listpack,skiplist(+hashtable for dict)

核心思想 ​:​小而美,大而强 ​。对于小规模数据,优先选择内存紧凑的线性结构(如 listpack, intset);一旦数据规模增长到影响性能的临界点,便果断切换到为大规模数据优化的复杂结构(如 hashtable, skiplist)。

理解这些底层原理,不仅能帮助我们在面试中脱颖而出,更能指导我们在实际项目中做出更明智的数据建模决策,从而充分发挥 Redis 的极致性能。

相关推荐
曹牧22 分钟前
Oracle数据库中,将JSON字符串转换为多行数据
数据库·oracle·json
被摘下的星星42 分钟前
MySQL count()函数的用法
数据库·mysql
末央&1 小时前
【天机论坛】项目环境搭建和数据库设计
java·数据库
徒 花1 小时前
数据库知识复习07
数据库·作业
素玥1 小时前
实训5 python连接mysql数据库
数据库·python·mysql
jnrjian1 小时前
text index 查看index column index定义 index 刷新频率 index视图
数据库·oracle
无敌昊哥战神1 小时前
深入理解 C 语言:巧妙利用“0地址”手写 offsetof 宏与内存对齐机制
c语言·数据结构·算法
瀚高PG实验室2 小时前
审计策略修改
网络·数据库·瀚高数据库
言慢行善2 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
韶博雅2 小时前
emcc24ai
开发语言·数据库·python