深入剖析Redis数据类型与底层数据结构
Redis 作为互联网架构中无可替代的高性能内存数据库 ,读写性能轻松突破10万+ QPS,其"快"的核心秘诀,除了经典的单线程模型、IO多路复用,更离不开底层精心设计的数据结构。
Redis 最精妙的设计在于:对外提供简洁易用的数据类型接口,对内根据数据特征动态选择最优底层实现,在时间复杂度与空间利用率之间做到极致平衡。
本文将从 Redis 5种核心数据类型出发,逐层拆解6种底层数据结构,结合原理、流程图、编码转换规则,带你彻底吃透 Redis 数据存储逻辑。
前言:核心关系总览
先建立最关键的认知,这是理解全文的基础:
- 对外数据类型(5种):String、List、Hash、Set、SortedSet,是开发者直接使用的操作接口;
- 底层数据结构(6种):哈希字典、SDS、压缩列表、双端链表、跳跃表、整数数组,是数据的真实存储载体;
- 动态编码转换:Redis 会根据数据量、值大小、数据类型,自动切换底层结构,无需手动干预。
一句话总结:数据类型是壳,底层结构是核。
一、Redis 5种核心数据类型(应用场景速查表)
在深入底层前,先快速掌握5种数据类型的核心特点与业务场景,建立直观认知:
| 数据类型 | 核心特点 | 典型应用场景 |
|---|---|---|
| String(字符串) | 二进制安全,支持字符串/数字/二进制,最大512MB | 用户信息缓存、分布式锁、计数器、接口限流 |
| List(列表) | 有序可重复,首尾操作极快,双向结构 | 消息队列、朋友圈时间轴、最新消息列表、栈/队列 |
| Hash(哈希) | 键值对集合,适合存储对象,支持单字段操作 | 用户详情、商品属性、系统配置项 |
| Set(集合) | 无序不可重复,支持交并差集运算 | 用户标签去重、共同好友、点赞去重、抽奖活动 |
| SortedSet(有序集合) | 按分值排序、不可重复,支持范围查询 | 排行榜、积分榜、延时队列、热度排序 |
二、深度拆解:Redis 6种底层数据结构(原理+流程图)
这是 Redis 高性能的核心,6种结构各司其职,适配不同的数据场景。
1. 哈希字典(Hash Dict):Redis 全局骨架
地位 :Redis 整个数据库的顶层结构,所有键值对最终都存储在全局哈希字典中,类似 Java 的 HashMap。
核心 :数组+链表,实现 O(1) 时间复杂度的查找。
核心机制
- 哈希桶:数组的每个元素就是一个哈希桶,通过 key 的哈希值定位数据;
- 哈希冲突:采用链式哈希解决,冲突的键值对用链表串联;
- 性能痛点 :链表过长会导致查询退化至 O(n),因此 Redis 设计了渐进式rehash。
关键:渐进式rehash(避免单线程阻塞)
Redis 维护两个哈希表(ht[0]、ht[1]),默认只用 ht[0],rehash 时分步拷贝数据,不阻塞业务命令:
否
是
否
是
初始状态
ht[0]存数据,ht[1]为空
触发rehash?
为ht[1]分配2倍空间
每次处理请求时,拷贝一个索引的数据
拷贝完成?
释放ht[0],ht[1]成为新表
✅ 优势:分散数据拷贝压力,保证 Redis 单线程不阻塞。
2. SDS(简单动态字符串):String 类型底层实现
Redis 没有使用 C 语言原生字符串,而是自定义了 SDS 结构,彻底解决了 C 字符串的性能缺陷。
C字符串 vs SDS 核心对比
| 对比维度 | C语言字符串 | Redis SDS |
|---|---|---|
| 长度获取 | 遍历字符串 O(n) | 内置 len 字段 O(1) |
| 内存分配 | 频繁分配释放 | 预分配+惰性释放 |
| 二进制安全 | 不支持(遇\0截断) | 支持(存储图片/视频) |
两大优化
- 空间预分配:修改字符串时,提前分配额外空间,减少内存分配次数;
- 惰性空间释放:缩短字符串时不回收内存,留作后续复用。
✅ 价值:让 String 类型的自增、拼接操作极致高效。
3. 压缩列表(ZipList):小数据场景空间王者
定位 :List、Hash、SortedSet 的轻量级实现,连续内存空间,无指针冗余,极致节省内存。
结构组成
[zlbytes][zltail][zllen][entry1][entry2]...[zlend]
- zlbytes:总字节数
- zltail:快速定位表尾
- entry:存储真实数据
- zlend:结束标记
适用条件(双小场景)
- List:元素<512个,值<64字节
- Hash:键值对<512个,键值长度<64字节
- SortedSet:元素<128个,值<64字节
❌ 缺陷 :插入/删除需要移动内存,效率低,只适合小数据。
4. 双端链表 + QuickList:List 类型的高效实现
当 List 数据量超过 ZipList 限制,Redis 会切换为链表结构。
- 双端链表:首尾操作 O(1),但内存碎片多;
- QuickList(Redis3.2+默认) :ZipList + 双端链表 的结合体,兼顾空间与效率。
✅ 优势:既拥有 ZipList 的内存紧凑性,又具备链表的高效首尾操作。
5. 跳跃表(SkipList):SortedSet 排序核心
SortedSet 能实现高效排序+范围查询 ,全靠跳跃表。
核心思想 :在链表基础上建立多层索引,实现O(logN) 查询效率,媲美红黑树,但实现更简单。
结构示意图
原始链表
第一层索引
第二层索引
10
50
10
30
50
10
20
30
40
50
查询逻辑
从顶层索引开始,快速跳过大量节点,逐层下沉定位目标,无需遍历全表。
✅ 优势:排序快、范围查询高效,是排行榜功能的核心支撑。
6. 整数数组(IntSet):Set 类型小整数优化
定位 :Set 类型的轻量化实现,专门存储整数+小数据量场景。
特点
- 有序、无重复整数数组;
- 根据整数大小自动选择编码(16/32/64位);
- 内存占用远小于哈希字典。
转换条件
- 所有元素都是整数;
- 元素个数 ≤ 512(默认配置)。
不满足条件时,自动切换为哈希字典。
三、核心桥梁:redisObject 与编码转换
Redis 通过 redisObject 对象,将对外数据类型 和底层结构关联起来,实现自动编码转换。
redisObject 核心字段
c
typedef struct redisObject {
unsigned type:4; // 数据类型(string/list等)
unsigned encoding:4; // 底层编码(结构)
void *ptr; // 指向底层数据
} robj;
5种数据类型编码转换规则(面试必考)
-
String
- int:存储整数
- embstr:短字符串(<44字节)
- raw:长字符串(≥44字节)
-
List
- ziplist:小数据
- quicklist:大数据(默认)
-
Hash
- ziplist:小数据
- hashtable:大数据
-
Set
- intset:纯整数+小数据
- hashtable:其他场景
-
SortedSet
- ziplist:小数据
- skiplist+hashtable:大数据
四、总结:Redis 数据结构核心思想
- 分层设计:对外简单易用,对内极致优化;
- 动态适配:根据数据特征自动切换底层结构,平衡速度与空间;
- 场景优先:没有最优结构,只有最适合当前场景的结构。
面试高频考点
- Redis 5种数据类型及应用场景;
- SDS 对比 C 字符串的优势;
- 渐进式rehash 的原理与作用;
- ZipList、QuickList、SkipList 的适用场景;
- 每种数据类型对应的底层编码及转换条件。
结语
Redis 的高性能绝非偶然,而是底层数据结构精心设计的结果。理解数据类型与底层结构的映射关系,不仅能应对面试,更能在实际开发中合理使用 Redis,避免因误用数据类型导致性能瓶颈。