Skip to content
清晨的一缕阳光
返回

Redis 架构设计与核心概念

Redis 是一个高性能的键值存储系统,广泛应用于缓存、消息队列、分布式锁等场景。本文将深入探讨 Redis 的架构设计、单线程模型和事件驱动机制,帮助你理解 Redis 高性能背后的核心原理。

一、Redis 架构概览

1.1 整体架构

Redis 采用经典的客户端 - 服务器(Client-Server)架构模型:

graph TB
    subgraph Clients
        C1[客户端 1]
        C2[客户端 2]
        C3[客户端 3]
    end
    
    subgraph Network
        TCP[TCP 连接]
    end
    
    subgraph Redis Server
        CMD[命令请求]
        AE[事件驱动]
        DB[(内存数据库)]
        PS[持久化]
    end
    
    C1 --> TCP
    C2 --> TCP
    C3 --> TCP
    TCP --> CMD
    CMD --> AE
    AE --> DB
    AE --> PS

1.2 核心组件

组件说明
网络模块处理 TCP 连接、协议解析
命令处理器解析并执行客户端命令
事件驱动基于 Reactor 模式的事件循环
内存管理对象分配、内存淘汰
持久化模块RDB 快照、AOF 日志

二、单线程模型

2.1 为什么选择单线程?

Redis 的核心网络 IO 和键值对读写采用单线程模型,这是 Redis 高性能的关键设计之一:

graph LR
    A[客户端请求] --> B[命令队列]
    B --> C[单线程处理]
    C --> D[执行命令]
    D --> E[返回结果]
    
    style C fill:#f9f,stroke:#333,stroke-width:2px

优势

  1. 无锁竞争 - 避免多线程的锁开销和上下文切换
  2. 实现简单 - 代码逻辑清晰,易于维护
  3. 原子性保证 - 单个命令天然原子执行
  4. 调试方便 - 单线程更容易排查问题

劣势

  1. CPU 多核利用不足 - 单个 Redis 实例无法充分利用多核
  2. 大 Key 阻塞 - 单个慢命令会阻塞所有请求

2.2 单线程性能分析

Redis 单线程能达到 10 万 + QPS 的原因:

因素说明
纯内存操作数据都在内存中,访问速度极快
高效数据结构SDS、跳表、压缩列表等优化设计
IO 多路复用epoll/kqueue 实现高并发网络 IO
协议简单RESP 协议解析高效

2.3 多线程 IO(Redis 6.0+)

Redis 6.0 引入了多线程 IO,但核心命令执行仍是单线程:

sequenceDiagram
    participant C as 客户端
    participant M as 主线程
    participant W as IO 工作线程
    
    C->>M: 建立连接
    M->>W: 分配读任务
    W->>W: 读取数据
    W->>M: 数据就绪
    M->>M: 执行命令
    M->>W: 分配写任务
    W->>W: 返回数据
    W->>C: 发送响应
    

注意:多线程仅用于网络 IO 的读写,命令执行仍然是单线程,保证了数据一致性。

三、事件驱动机制

3.1 Reactor 模式

Redis 基于 Reactor 模式实现事件驱动:

stateDiagram-v2
    [*] --> 文件事件
    文件事件 --> AE: 连接请求
    文件事件 --> AE: 读请求
    文件事件 --> AE: 写请求
    AE --> 命令处理: 执行命令
    AE --> 网络回复: 返回结果
    时间事件 --> AE: 定时任务
    时间事件 --> 持久化:RDB/AOF

3.2 文件事件

Redis 使用 IO 多路复用技术(epoll/kqueue)监听多个 socket 连接:

// 简化的事件循环伪代码
while (!stop) {
    // 1. 等待事件发生
    aeApiPoll(eventLoop, timeout);
    
    // 2. 处理已发生的事件
    for (each fired event) {
        if (readable) {
            acceptConnection();  // 接受连接
            readCommand();       // 读取命令
        }
        if (writable) {
            writeResponse();     // 写入响应
        }
    }
    
    // 3. 执行时间事件
    processTimeEvents();
}

3.3 时间事件

Redis 的时间事件用于处理定时任务:

任务说明
serverCron每秒执行,更新统计信息、清理过期 key
RDB 定时保存根据配置触发快照
AOF 重写定期压缩 AOF 文件
超时检测客户端连接超时、集群节点超时

四、内存数据库设计

4.1 全局数据结构

Redis 服务器状态的全局结构:

struct redisServer {
    // 数据库数组
    redisDb *db;
    int dbnum;              // 数据库数量
    
    // 网络
    aeEventLoop *el;        // 事件循环
    list *clients;          // 客户端列表
    
    // 持久化
    int rdb_save_time;      // RDB 保存状态
    int aof_state;          // AOF 状态
    
    // 统计
    long long stat_commands_processed;
    long long stat_net_input_bytes;
    long long stat_net_output_bytes;
};

4.2 数据库结构

每个 Redis 数据库包含:

graph TB
    subgraph RedisDB
        D1[字典 - 键空间]
        D2[字典 - 过期时间]
        L1[阻塞列表]
        N1[键空间通知]
    end
    
    D1 --> K1[Key1: Value1]
    D1 --> K2[Key2: Value2]
    D2 --> E1[Key1: Expire]

核心组件

4.3 过期策略

Redis 采用惰性删除 + 定期删除相结合的策略:

flowchart TD
    A[访问 Key] --> B{是否过期?}
    B -->|是 | C[删除 Key]
    B -->|否 | D[返回 Value]
    
    E[定期任务] --> F[随机采样 N 个 Key]
    F --> G{有过期 Key?}
    G -->|是 | H[删除过期 Key]
    G -->|否 | I[结束]
    H --> J{删除比例 > 25%?}
    J -->|是 | F
    J -->|否 | I

五、网络协议

5.1 RESP 协议

Redis 使用 RESP(Redis Serialization Protocol)协议:

类型格式示例
简单字符串++OK\r\n
错误--ERR unknown\r\n
整数::1000\r\n
批量字符串$$5\r\nhello\r\n
数组**2\r\n$3\r\nGET\r\n$3\r\nkey\r\n

5.2 请求 - 响应流程

sequenceDiagram
    participant Client
    participant Redis
    
    Client->>Redis: *2\r\n$3\r\nGET\r\n$3\r\nkey\r\n
    Redis->>Redis: 解析命令
    Redis->>Redis: 查找 key
    Redis-->>Client: $5\r\nvalue\r\n

六、性能优化要点

6.1 避免阻塞命令

危险命令风险替代方案
KEYS *遍历所有 key使用 SCAN
HGETALL大 Hash 阻塞使用 HSCAN
SMEMBERS大 Set 阻塞使用 SSCAN
DEL big-key删除大 key 阻塞使用 UNLINK

6.2 合理设置内存淘汰策略

# redis.conf 配置示例
maxmemory 2gb
maxmemory-policy allkeys-lru  # 推荐生产环境使用

常用淘汰策略

策略说明适用场景
noeviction不淘汰,写操作返回错误不允许数据丢失
allkeys-lru淘汰最近最少使用的 key缓存场景
volatile-lru淘汰有过期时间的 key部分数据需要持久
allkeys-random随机淘汰访问均匀分布

6.3 Pipeline 批量操作

# ❌ 低效:N 次网络往返
for i in range(1000):
    redis.set(f"key:{i}", i)

# ✅ 高效:1 次网络往返
pipe = redis.pipeline()
for i in range(1000):
    pipe.set(f"key:{i}", i)
pipe.execute()

七、总结

核心要点回顾

  1. 单线程模型 - 避免锁竞争,命令原子执行
  2. 事件驱动 - Reactor 模式实现高并发
  3. 内存操作 - 高效数据结构保证性能
  4. IO 多路复用 - epoll/kqueue 监听多连接
  5. 多线程 IO - Redis 6.0+ 提升网络吞吐

架构设计启示

设计原则Redis 实践
简单即高效单线程减少复杂度
空间换时间内存存储 + 丰富数据结构
异步非阻塞事件驱动 + IO 多路复用
渐进式优化惰性删除 + 定期删除

参考资料


分享这篇文章到:

上一篇文章
Redis 应用场景与选型指南
下一篇文章
AI 应用成本控制实战