Redis 架构与持久化

本文最后更新于 2025年9月10日 下午

1. Redis线程模型概述

1.1 单线程设计哲学

Redis在设计上采用了单线程处理数据业务的架构,这一设计带来了显著优势:

  • 避免线程间上下文切换开销 :减少CPU在线程切换时的性能损耗
  • 无需考虑并发控制 :不存在多线程访问共享数据的竞态条件问题
  • 简化数据操作 :业务处理线程成为无状态服务,逻辑更加清晰
  • 高性能保证 :基于内存操作和高效的数据结构,单线程足以应对高并发场景

1.2 版本演进

  • Redis 6.0之前 :完全单线程模型,I/O和业务处理均在主线程
  • Redis 6.0及以后 :引入多线程I/O,但核心数据操作仍保持单线程

这种混合模式既保持了单线程的简洁性,又通过多线程I/O提升了网络处理效率。

2. Reactor模型详解

2.1 什么是Reactor模型

Reactor模型是一种 事件驱动的I/O多路复用架构 ,其核心思想是:

  • 在单个线程中监听多个I/O资源
  • 使用多路复用器(select/poll/epoll)阻塞等待事件
  • 当有事件就绪时,分发给相应的处理器处理

2.2 单线程Reactor模式

1
2
3
4
┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│ Client │───▶│ Reactor │───▶│ Handler │
│ Requests │ │ (epoll) │ │ (Business) │
└─────────────┘ └──────────────┘ └─────────────┘

实现流程:

  1. 创建监听socket并注册到epoll
  2. 设置事件监听类型(EPOLLIN等)
  3. epoll_wait阻塞等待事件
  4. 接收连接请求时,创建新的连接socket并注册
  5. 处理连接socket的读写事件
  6. 调用相应的业务处理函数

优点: 简单、无锁
缺点: 无法充分利用多核CPU资源

2.3 多线程Reactor模式

1
2
3
4
┌─────────────┐    ┌──────────────┐    ┌─────────────┐
Client │───▶│ Reactor │───▶│ Thread Pool
│ Requests │ │ (epoll) │ │ (Handlers) │
└─────────────┘ └──────────────┘ └─────────────┘

改进:

  • 主线程负责I/O事件监听
  • 工作线程池负责业务处理
  • I/O与业务处理解耦
  • 减少线程创建/销毁开销

2.4 主从Reactor模式

1
2
3
4
┌─────────────┐    ┌──────────────┐    ┌─────────────┐    ┌─────────────┐
Client │───▶│ Main Reactor │───▶│Sub Reactor │───▶│ Thread Pool
Requests │ │ (Accept) │ │ (I/O Events)│ │ (Handlers)
└─────────────┘ └──────────────┘ └─────────────┘ └─────────────┘

进一步优化:

  • 主Reactor专门处理连接接受
  • 从Reactor专门处理已建立连接的I/O事件
  • 实现更好的负载均衡和扩展性

2.5 Redis中的Reactor实现

  • Redis 6.0之前 :采用单线程Reactor模式
  • Redis 6.0之后 :I/O使用多线程,业务处理保持单线程

3. Redis核心组件架构

3.1 redisServer - 服务器核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct redisServer {
// 网络配置
int port; // 监听端口
char *bindaddr[16]; // 绑定地址数组
int io_threads; // I/O线程数量

// 功能组件
dict *commands; // 命令注册表
int ipfd[16]; // 监听socket文件描述符
aeEventLoop *el; // 事件循环器

// 数据存储
redisDb *db; // 数据库数组

// 客户端管理
list *clients; // 当前连接的客户端列表
list *clients_pending_write; // 待写响应的客户端列表
};

3.2 redisDb - 数据库结构

Redis默认包含16个数据库(DB0-DB15),每个数据库结构如下:

1
2
3
4
5
typedef struct redisDb {
dict *dict; // 主要键值对存储
dict *expires; // 键过期时间存储
int id; // 数据库编号(0-15)
} redisDb;

设计亮点:

  • 使用字典(哈希表)实现O(1)平均查找时间
  • 独立的过期时间存储,支持TTL功能
  • 多数据库隔离,支持SELECT命令切换

3.3 redisCommand - 命令系统

1
2
3
4
5
6
7
8
struct redisCommand {
char *name; // 命令名称
redisCommandProc *proc; // 命令处理函数指针
int arity; // 参数个数
char *sflags; // 命令标志
int flags; // 编译后的标志
// ... 其他字段
};

3.4 client - 客户端连接

1
2
3
4
5
6
7
typedef struct client {
int fd; // socket文件描述符
redisDb *db; // 当前选择的数据库
char buf[PROTO_REPLY_CHUNK_BYTES]; // 响应缓冲区
listNode *client_list_node; // 在客户端列表中的节点
// ... 其他字段
} client;

3.5 aeEventLoop - 事件循环系统

1
2
3
4
5
6
7
8
typedef struct aeEventLoop {
int maxfd; // 当前最大文件描述符
int setsize; // 事件集合大小
aeFileEvent *events; // 注册的文件事件数组
aeFiredEvent *fired; // 就绪事件数组
aeBeforeSleepProc *beforesleep; // 睡眠前执行的函数
// ... 其他字段
} aeEventLoop;

4. Redis启动流程详解

4.1 启动命令

1
./redis-server redis.conf

4.2 初始化步骤

1
2
3
4
5
6
7
8
9
10
int main(int argc, char **argv) {
// 1. 解析配置文件
loadServerConfig(configfile, options);

// 2. 初始化服务器结构
initServer();

// 3. 启动事件循环
aeMain(server.el);
}

4.3 详细初始化过程

  1. 命令注册
    • 初始化所有支持的Redis命令
    • 将命令名称与处理函数绑定
    • 构建命令查找字典
  2. 数据库初始化
    • 创建16个redisDb实例
    • 初始化每个数据库的字典结构
  3. 网络初始化
    • 创建监听socket
    • 绑定到指定端口和地址
    • 开始监听连接请求
  4. 事件系统初始化
    • 创建epoll实例
    • 注册监听socket的可读事件
    • 设置acceptTcpHandler为连接处理函数

4.4 主事件循环

1
2
3
4
5
6
7
8
9
10
11
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
// 处理待写客户端响应
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);

// 等待I/O事件
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}

4.5 请求处理流程

  1. 连接接受
    • epoll_wait检测到监听socket可读
    • 调用acceptTcpHandler
    • 创建client结构,注册到epoll
  2. 命令处理
    • 从socket读取命令字符串
    • 解析命令和参数
    • 在commands字典中查找处理函数
    • 执行命令并生成响应
  3. 响应发送
    • 将结果写入client->buf
    • 将client加入clients_pending_write列表
    • 在下次循环前发送响应

5. Redis持久化机制

5.1 RDB持久化

RDB(Redis Database Backup)是Redis的快照式持久化方式。

5.1.1 工作原理

  • 在特定时间点创建数据集的完整快照
  • 生成二进制格式的dump.rdb文件
  • 恢复时直接加载快照文件

5.1.2 触发方式

手动触发:

1
2
SAVE        # 阻塞主线程进行保存
BGSAVE # 后台子进程保存,推荐方式

自动触发:

1
2
3
4
# redis.conf配置示例
save 900 1 # 900秒内至少1次修改时触发
save 300 10 # 300秒内至少10次修改时触发
save 60 10000 # 60秒内至少10000次修改时触发

5.1.3 优缺点分析

优点:

  • 文件紧凑,适合备份和灾难恢复
  • 恢复速度快,适合大数据集
  • 对Redis性能影响较小(使用fork子进程)

缺点:

  • 数据完整性较差,可能丢失最后一次快照后的数据
  • 数据集较大时fork过程可能阻塞服务
  • 不适合实时性要求高的场景

5.2 AOF持久化

AOF(Append Only File)是Redis的日志式持久化方式。

5.2.1 工作原理

  • 记录每个写操作命令
  • 以文本格式追加到AOF文件
  • 恢复时重新执行所有命令

5.2.2 文件结构

1
2
3
appendonly.aof.1.base.aof     # 基础AOF文件(用于重写)
appendonly.aof.1.incr.aof # 增量AOF文件(记录新命令)
appendonly.aof.manifest # 清单文件(管理AOF文件)

5.2.3 写回策略

配置参数: appendfsync

策略 说明 性能 安全性
always 每个命令立即同步写入磁盘 最高
everysec 每秒批量写入磁盘(默认推荐 中等 较高
no 由操作系统决定写入时机 较低

5.2.4 AOF重写机制

问题: AOF文件会随着时间增长变得很大

解决: AOF重写(Rewrite)

  • 分析当前内存中的数据状态
  • 生成达到相同状态所需的最少命令集
  • 替换原有的庞大AOF文件

触发方式:

1
2
3
4
5
6
# 手动触发
BGREWRITEAOF

# 自动触发配置
auto-aof-rewrite-percentage 100 # 大小翻倍时触发
auto-aof-rewrite-min-size 64mb # 最小64MB时才考虑重写

重写流程:

  1. Redis创建新的增量AOF文件
  2. 后续写操作记录到新增量文件
  3. 子进程将内存数据写成新的基础AOF文件
  4. 原子性地替换旧文件

5.3 混合持久化模式

Redis 4.0引入的混合持久化结合了RDB和AOF的优势:

5.3.1 工作机制

  • AOF重写时 :将当前内存数据以RDB格式写入AOF文件开头
  • 增量部分 :仍以AOF格式追加写命令
  • 恢复时 :先加载RDB部分,再重放AOF部分

5.3.2 启用配置

1
aof-use-rdb-preamble yes  # 启用混合持久化

5.3.3 优势分析

  • 快速恢复 :RDB格式的基础数据加载速度快
  • 数据完整性 :AOF格式的增量数据保证不丢失
  • 文件大小 :相比纯AOF文件更紧凑

6. 性能优化建议

6.1 持久化策略选择

场景 推荐策略 理由
高性能缓存 仅RDB 性能最优,允许少量数据丢失
重要业务数据 AOF everysec 平衡性能与数据安全
金融交易等 AOF always 数据安全优先
大数据集 混合模式 兼顾性能与安全

6.2 配置优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 内存策略
maxmemory 2gb
maxmemory-policy allkeys-lru

# 持久化优化
save 900 1
stop-writes-on-bgsave-error yes
rdbcompression yes

# AOF优化
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

6.3 监控要点

  • 内存使用率 :避免内存不足导致的性能下降
  • 持久化延迟 :监控RDB和AOF的执行时间
  • 网络I/O :关注连接数和请求响应时间
  • 命令统计 :分析慢查询和热点命令

7. 总结

Redis通过精心设计的架构实现了高性能和高可用性:

  1. 单线程业务模型 :简化并发控制,提升性能
  2. Reactor网络模型 :高效的I/O多路复用
  3. 灵活的持久化 :RDB、AOF、混合模式满足不同需求
  4. 优雅的组件设计 :清晰的职责分离和模块化架构

理解这些核心概念和机制,有助于更好地使用和优化Redis在生产环境中的表现。


Redis 架构与持久化
http://gadoid.io/2025/09/10/Redis-架构与持久化/
作者
Codfish
发布于
2025年9月10日
许可协议