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

相关推荐
苍煜15 分钟前
慢SQL优化实战教学
java·数据库·sql
zhaoyong2221 小时前
MySQL 存储过程中字符集与排序规则不匹配导致查询性能下降的解决方案
jvm·数据库·python
sinat_383437361 小时前
golang如何从Python转型Go开发_golang从Python转型Go开发攻略
jvm·数据库·python
远洪1 小时前
claude code 国内安装使用
数据库·mysql
雨辰AI1 小时前
SpringBoot3 + 人大金仓 V9 微服务监控实战|Prometheus+Grafana+SkyWalking 全链路监控
数据库·后端·微服务·grafana·prometheus·skywalking
二哈赛车手1 小时前
新人笔记---ES和kibana启动问题以及一些常用的linux的错误排查方法,以及ES,数据库泄密解决方案[超详细]
java·linux·数据库·spring boot·笔记·elasticsearch
myrh pdmd1 小时前
maven导入spring框架
数据库·spring·maven
xvhao20131 小时前
单源、多源最短路
数据结构·c++·算法·深度优先·动态规划·图论·图搜索算法
爬山算法2 小时前
MongoDB(118)如何在升级过程中进行数据备份?
数据库·mongodb·oracle
千月落2 小时前
Redis数据迁移
数据库·redis·缓存