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 的极致性能。

相关推荐
m0_730115112 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
qq_410194292 小时前
SQL语句性能优化
数据库·sql·性能优化
小江的记录本2 小时前
【MyBatis-Plus】Spring Boot + MyBatis-Plus 进行各种数据库操作(附完整 CRUD 项目代码示例)
java·前端·数据库·spring boot·后端·sql·mybatis
wanhengidc2 小时前
《三国志异闻录》搬砖新游戏 云手机
运维·服务器·数据库·游戏·智能手机
逆境不可逃2 小时前
LeetCode 热题 100 之 33. 搜索旋转排序数组 153. 寻找旋转排序数组中的最小值 4. 寻找两个正序数组的中位数
java·开发语言·数据结构·算法·leetcode·职场和发展
2301_807367192 小时前
Python日志记录(Logging)最佳实践
jvm·数据库·python
2301_795741793 小时前
构建一个基于命令行的待办事项应用
jvm·数据库·python
FITA阿泽要努力3 小时前
《实战SQL: GROUP BY》
数据库·sql
sw1213893 小时前
Python字典与集合:高效数据管理的艺术
jvm·数据库·python