【Redis】底层原理解析(SDS / 跳表 / IO多路复用 / 单线程模型)
·
Redis 底层原理全解析(SDS / 跳表 / IO多路复用 / 单线程模型)
📌 本篇目标:
- 从“会用 Redis” → “理解 Redis 为什么这么设计”
- 掌握面试中的“底层原理问题”
- 建立源码级思维
一、Redis 为什么快?
在前面我们说过 Redis 快,但这一篇我们从底层重新理解:
✅ 本质原因(升级版)
Redis 快 = 数据结构优化 + IO模型优化 + 架构设计优化
拆解为 4 大核心
1. 高效数据结构(SDS / 跳表)
2. 内存操作(无磁盘IO)
3. IO多路复用(高并发)
4. 单线程模型(无锁)
二、Redis 字符串底层:SDS
问题:为什么不用 C 原生字符串?
C 字符串:
char str[] = "hello";
问题:
- ❌ 需要遍历才能知道长度(O(n))
- ❌ 容易缓冲区溢出
- ❌ 不支持动态扩展
✅ Redis 解决方案:SDS(Simple Dynamic String)
SDS 结构(核心)
struct sdshdr {
int len; // 已使用长度
int free; // 剩余空间
char buf[]; // 数据
}
优势
1️⃣ 获取长度 O(1)
直接读取 len
2️⃣ 防止缓冲区溢出
自动扩容
3️⃣ 空间预分配
如果扩容 → 多分配一部分空间
好处:
减少频繁内存分配
4️⃣ 惰性释放
删除后不立即回收空间
面试回答模板
Redis 使用 SDS 替代 C 字符串,
优势:
1. O(1) 获取长度
2. 防止缓冲区溢出
3. 空间预分配 + 惰性释放
三、Redis 有序集合底层:跳表(SkipList)
为什么不用红黑树?
Redis 选择跳表的原因:
实现简单 + 性能接近平衡树
跳表结构理解
Level 3: 1 ----------- 9
Level 2: 1 ------ 5 -- 9
Level 1: 1 -- 3 -- 5 -- 7 -- 9
数据结构组成
每个节点包含:
- Key:存储的数据值(或分数)。
- Forward Pointers:一个数组,指向该节点在每一层的下一个节点。
- Level:该节点拥有的层数(高度)。
类似“多层索引”
查找过程
从最高层 `max_level`开始:
1. 在当前层向右遍历,直到下一个节点的值大于等于目标值。
2. 如果当前节点值等于目标值,返回成功。
3. 否则,**下降一层**,重复步骤 1。
4. 如果下降到第 0 层仍未找到,返回失败。
时间复杂度
查询 / 插入 / 删除:O(log n)
Redis 中的应用
ZSet(有序集合)
面试回答模板
ZSet 使用跳表 + hash 表实现:
- 跳表 → 排序
- hash → 快速查找
四、Redis List 底层:QuickList
为什么不用普通链表?
问题:
- ❌ 内存碎片多
- ❌ 访问慢
✅ QuickList(链表 + 压缩列表)
LinkedList + ZipList
结构
[ziplist] <-> [ziplist] <-> [ziplist]
优势
- 节省内存
- 保持链表灵活性
五、Redis IO 模型:IO 多路复用
问题:Redis 如何处理高并发?
👉 单线程怎么处理上万连接?
✅ 答案:IO 多路复用
类比理解
一个服务员同时负责多张桌子
常见实现
- select(轮询通知)
- poll(改进的轮询通知)
- epoll(Linux 事件回调)
| 特性 | Select | Poll | Epoll |
|---|---|---|---|
| 底层机制 | 轮询(线性扫描) | 轮询(线性扫描) | 回调/就绪列表 |
| 效率 | 连接数多时,效率O(n),低下 | 连接数多时,效率O(n),低下 | 效率O(1),与活跃连接数相关,高并发下极高效 |
| 最大连接数 | 有限制(FD_SETSIZE,通常1024) | 理论上无硬性限制 | 无硬性限制,受系统资源限制 |
| 工作模式 | 仅水平触发 | 仅水平触发 | 支持水平触发和边缘触发 |
| 内存与数据拷贝 | 每次调用需在内核和用户空间拷贝整个fd集合 | 每次调用需拷贝整个pollfd数组 | 使用内存映射,共享内存,减少拷贝开销 |
| 使用复杂度 | 中 | 中 | 较高(需管理epoll实例) |
| 跨平台 | 几乎所有平台 | 大部分Unix-like系统 | Linux特有 |
工作流程
1. 监听多个 socket
2. 哪个有数据 → 处理哪个
优势
- 不需要多线程
- 高效处理大量连接
面试回答模板
Redis 使用 IO 多路复用(epoll),
可以在单线程下高效处理大量连接请求
六、Redis 单线程模型
Redis 真的是单线程吗?
👉 面试陷阱题!
✅ 正确答案:
核心命令执行是单线程
但:
- 网络 IO → 多路复用
- 持久化 → 子线程
为什么用单线程?
1️⃣ 避免锁竞争
多线程 → 加锁 → 性能下降
2️⃣ 减少上下文切换
3️⃣ 内存操作本身就很快
❗ 面试加分点
Redis 性能瓶颈 ≠ CPU
而是:内存 + 网络
七、Redis 内存淘汰策略
问题:内存满了怎么办?
✅ 8 种策略(重点记 3 个)
| 策略 | 说明 |
|---|---|
| noeviction | 不删除 |
| allkeys-lru ⭐ | 最近最少使用 |
| allkeys-random | 随机 |
| volatile-lru | 有过期的 key 中淘汰 |
推荐
allkeys-lru(最常用)
八、Redis 过期删除策略
Redis 如何删除过期 key?
✅ 三种方式
1️⃣ 定时删除 ❌(不用)
2️⃣ 惰性删除
用到才检查
3️⃣ 定期删除
随机抽查
面试总结
Redis 使用:惰性删除 + 定期删除
九、Redis 线程模型演进
Redis 6 之后变化
引入:
多线程处理 IO
但:
命令执行仍然是单线程
十、终极面试总结
Redis 为什么这么快?
1. 内存操作
2. 高效数据结构(SDS / 跳表)
3. IO 多路复用
4. 单线程避免锁竞争
Redis 底层核心结构
String → SDS
List → QuickList
ZSet → 跳表
Redis 线程模型
核心单线程 + IO多路复用 + 后台线程
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)