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多路复用 + 后台线程
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐