# 27. Redis 使用的是哪种内存模型,如何支持高并发的写入与读取?

# 标准答案

Redis 采用 基于内存(In-Memory)的数据存储模型,主要依赖 跳表(SkipList)、哈希表(HashTable)、压缩列表(ZipList)和整数集合(IntSet) 等数据结构进行存储。为了支持高并发访问,Redis 采用了 单线程事件循环(Reactor 模型) 处理客户端请求,基于 I/O 多路复用(epoll/kqueue) 机制,实现了 高效的非阻塞并发处理

此外,Redis 通过 多种数据结构优化、流水线(Pipeline)、持久化(AOF/RDB)、复制与分片 来提高读写吞吐量,同时利用 乐观锁(CAS)、复制偏移量、延迟队列 等机制来保证一致性。

# 答案解析

# 1️⃣ Redis 采用的内存存储模型

Redis 是 纯内存数据库,其数据模型主要基于 键值存储(Key-Value Store),但其值(Value)不仅限于字符串,还支持丰富的数据结构,如 列表、集合、有序集合、哈希等。底层数据存储依赖 多种优化过的数据结构,以提高查询和修改的效率:

数据结构 适用场景 主要特性
SDS(简单动态字符串) 存储字符串 变长字符串,减少内存碎片
哈希表(HashTable) 字典存储 O(1) 读写,扩容采用渐进式 rehash
跳表(SkipList) 有序集合(SortedSet) O(logN) 查询,支持范围查找
压缩列表(ZipList) 短哈希表 / 短列表 连续内存,减少内存开销
整数集合(IntSet) 小规模整数集合 低内存占用,支持自动转换

Redis 通过 智能数据结构选择策略 来减少内存占用,例如:

  • 小规模 Hash / List / Set 会使用 ZipList 或 IntSet 代替标准数据结构,降低指针开销。
  • 哈希表采用渐进式 rehash,避免阻塞操作。

# 2️⃣ Redis 高并发读写的核心机制

Redis 采用 单线程 + I/O 多路复用 处理请求,结合 多种优化技术 来支持高并发:

# 🔹(1)单线程事件循环

Redis 主线程采用单线程处理客户端请求,避免了线程上下文切换、锁竞争等问题,提供了更高的 CPU 缓存命中率,提升了性能。

🔹 为什么单线程反而能高效处理请求?

  1. Redis 主要是内存操作(O(1) / O(logN)),CPU 不是瓶颈,而是网络 I/O 和系统调用的开销更大。
  2. 避免多线程锁竞争,减少加锁/解锁的开销,提高 CPU 利用率。
  3. I/O 多路复用(epoll/kqueue)支持同时处理大量并发连接。

# 🔹(2)I/O 多路复用

Redis 使用 epoll(Linux)或 kqueue(macOS) 进行 非阻塞 I/O 处理

  • 采用 事件驱动机制(Reactor 模型),通过 select/poll/epoll/kqueue 等系统调用来监听多个连接,避免了传统阻塞 I/O 的低效
  • 事件类型包括:可读事件、可写事件、定时任务、文件事件等,采用 事件循环 进行调度。

示例:Linux epoll 监听多个 socket 事件:

epoll_create(); // 创建 epoll 事件监听
epoll_ctl();    // 添加 Redis 客户端 socket 到 epoll
epoll_wait();   // 监听多个事件,减少 CPU 轮询开销
1
2
3

🔹 优点:

  • 减少了 CPU 轮询消耗(不像 select/poll 需要遍历整个事件列表)。
  • 避免了线程切换(单线程即可高效处理数万级别的并发连接)。

# 🔹(3)Redis 的读写优化

Redis 采用 数据结构优化 + 异步处理 来提高吞吐量:

机制 作用 优势
Pipeline(流水线) 合并多个命令,减少 RTT 降低网络延迟
Lazy-Free(惰性删除) 异步删除大对象 避免阻塞主线程
QuickList(快速链表) 组合 ZipList 和 LinkedList 减少指针占用,提高缓存命中
HyperLogLog 近似去重 仅用 12KB 统计 10 亿级数据

# 3️⃣ Redis 在高并发下的读写一致性

# 🔹(1)Redis 复制机制

  • 主从复制:采用 异步复制,主节点处理写请求后,异步同步到从节点,提高读吞吐
  • 偏移量控制:主从之间维护 复制偏移量,保证增量同步。
  • Gossip 协议:Redis Cluster 通过 Gossip 机制 定期同步元数据,保证数据一致性。

问题:主从切换可能导致数据丢失?

  • 解决方案WAIT 机制,要求主节点等待至少 N 个从节点确认写入后才返回成功。

# 🔹(2)一致性控制

机制 作用 适用场景
事务(MULTI/EXEC) 批量执行多个命令,保证原子性 适用于小规模事务
WATCH 机制 监视键值变化,防止并发修改 适用于乐观锁场景
CAS(Compare-And-Swap) 结合 GET & SETNX 实现原子更新 分布式锁场景

示例:使用 WATCH 进行 CAS 操作:

WATCH counter
MULTI
INCR counter
EXEC
1
2
3
4

如果 counter 发生变更,事务 EXEC 将失败,需重试。

# 4️⃣ Redis 的高并发优化

Redis 采用 数据分片 + 复制 + 多线程 I/O 进行扩展:

  1. 分片(Sharding):Redis Cluster 采用 哈希槽(Hash Slot)机制 进行数据分布,避免单点瓶颈。
  2. 多线程 I/O(Redis 6.0+)
    • 仅限于 网络 I/O 线程,核心逻辑仍然是单线程。
    • io-threads 配置 开启多线程,提高网络吞吐:
      io-threads 4
      
      1
    • 实测 吞吐量提高 2~3 倍,降低延迟

# 深入追问

🔹 Redis 为什么采用单线程模型?它的瓶颈在哪?
🔹 Redis 的 epoll 和 Go 语言的 goroutine 调度模型有什么不同?
🔹 Redis 如何处理大对象删除时的阻塞问题?(惰性删除 vs 主动删除)

# 相关面试题

🔹 Redis 为什么单线程还能支撑高并发?
🔹 Redis 如何处理海量 Key 查询的高效索引?
🔹 Redis 的数据淘汰策略有哪些?如何优化内存占用?

# 总结

Redis 采用 基于内存(In-Memory)的数据模型,通过 高效数据结构(哈希表、跳表、压缩列表) 进行存储,并结合 单线程事件循环 + I/O 多路复用 来支持高并发读写。为了进一步优化吞吐,Redis 采用 Pipeline、异步删除、主从复制、分片(Cluster)、多线程 I/O(6.0+) 等技术,确保在高并发场景下依然具备高性能、低延迟 的特点。