十七、Redis 核心原理与架构详解

Redis 是一个开源的、基于内存的高性能键值存储数据库,支持丰富的数据结构类型,单节点 QPS 可达 10 万+,读写延迟低至亚毫秒级别。本文从 Redis 的核心架构、数据结构、持久化机制、内存管理、高可用方案等维度进行系统性的深度剖析。


一、Redis 概述

1.1 什么是 Redis

Redis(Remote Dictionary Server)是一个基于内存的键值存储系统,由 Salvatore Sanfilippo(antirez)于 2009 年使用 C 语言编写。它属于 NoSQL 数据库中的键值存储类,但远不止是一个简单的 KV 缓存------Redis 提供了 String、List、Hash、Set、ZSet、Stream、JSON、Bitmap、HyperLogLog、Geospatial 等丰富的数据结构,使其可以同时作为数据库、缓存、消息中间件和流式引擎使用。

1.2 Redis 的核心定位

定位 说明
内存数据库 所有数据存储在内存中,提供亚毫秒级读写延迟
缓存引擎 作为关系数据库前的缓存层,大幅降低 DB 压力
消息中间件 支持 Pub/Sub 发布订阅和 Stream 消息流
流式引擎 Stream 数据类型支持消息持久化、消费组和 ACK 机制
向量数据库 Redis 7.4+ 支持向量集(Vector Sets),可用于语义搜索和 RAG

1.3 Redis 为什么快

Redis 的高性能是五个层面协同作用的结果:

层面 核心机制 对性能的贡献
存储介质 纯内存操作 消除磁盘 IO 瓶颈,延迟从 ms 级降到 μs 级
数据结构 精心设计的底层编码 同一 API 根据数据规模自动切换最优编码
IO 模型 epoll 多路复用 单线程处理数万并发连接,避免线性扫描
线程模型 核心单线程 无锁设计,消除上下文切换和竞争开销
协议设计 RESP 协议 文本协议解析快速,支持 Pipeline 批量操作

二、Redis 核心架构

2.1 整体架构分层

Redis 的架构可以分为四层:

客户端层:客户端通过 RESP(Redis Serialization Protocol)协议与服务端通信。RESP 是一种文本协议,格式简单、解析快速,支持五种数据类型:简单字符串(+)、错误(-)、整数(:)、批量字符串($)和数组(*)。

网络层 :Redis 使用基于 epoll 的 IO 多路复用模型,单线程即可处理数万个并发连接。核心组件 aeEventLoop 负责监听文件描述符事件(可读/可写/超时),并将事件分发给对应的处理器。

核心层:包含命令处理器、数据库字典、过期字典和内存分配器。每个 Redis 实例默认有 16 个数据库(编号 0-15),每个数据库维护两个字典:

  • 键空间字典 (key space dict):存储所有键值对,key 为字符串,value 为 redisObject
  • 过期字典(expires dict):存储键的过期时间戳,key 指向键空间中的同一个键,value 为 Unix 时间戳

持久化层:RDB 快照和 AOF 日志两种机制确保数据持久性。

2.2 单线程模型解析

Redis 的核心命令处理是单线程的(Redis 6.0 之前完全单线程,6.0 引入多线程仅用于网络 IO 解析):

复制代码
┌──────────────────────────────────────────────┐
│               Redis 事件循环                   │
│  ┌─────────┐  ┌──────────┐  ┌─────────────┐  │
│  │ 文件事件 │  │ 时间事件 │  │ 命令执行器   │  │
│  │ (epoll) │  │(serverCron)│ │ (processCmd)│  │
│  └────┬────┘  └────┬─────┘  └──────┬──────┘  │
│       │            │               │          │
│  读写客户端数据  定时任务调度    执行具体命令    │
│  协议解析/编码  过期key清理    数据操作         │
│  复制同步      AOF重写触发    持久化            │
└──────────────────────────────────────────────┘

为什么单线程还这么快?

  1. 纯内存操作:所有操作都在内存中完成,CPU 是唯一的性能瓶颈,单线程避免了多线程的锁竞争和上下文切换
  2. 高效数据结构:精心设计的跳表、压缩列表、哈希表等,保证操作时间复杂度为 O(1) 或 O(logN)
  3. IO 多路复用:epoll 机制使单线程能同时监听数千个连接的 IO 事件
  4. RESP 协议简单:文本协议解析开销极低
  5. Pipeline 支持:客户端可以批量发送命令,减少网络 RTT

Redis 6.0 多线程改动:Redis 6.0 引入了多线程网络 IO(默认关闭),将协议解析和响应编码从主线程卸载到 IO 线程,但命令执行仍然是单线程的,保证原子性不变。

2.3 RESP 协议

RESP 协议是 Redis 客户端与服务端通信的基础:

复制代码
# 客户端请求(RESP 数组格式)
*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n

# 服务端响应
+OK\r\n                    # 简单字符串
-Error message\r\n         # 错误
:1000\r\n                  # 整数
$5\r\nhello\r\n            # 批量字符串
*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n  # 数组

# RESP3 新增类型(Redis 6.0+)
%2\r\n                     # Map 类型
~3\r\n                     # Set 类型
=15\r\n...txt\r\n          # Verbatim 字符串

三、Redis 数据结构

3.1 两层架构设计

Redis 最精妙的设计之一是"两层架构"------对外暴露 5 种核心逻辑数据类型,底层根据数据量和元素大小自动选择最优的物理编码。这两层通过 redisObject 结构体桥接:

c 复制代码
typedef struct redisObject {
    unsigned type:4;      // 逻辑类型(STRING/LIST/HASH/SET/ZSET)
    unsigned encoding:4;  // 物理编码(int/embstr/raw/hashtable/skiplist...)
    unsigned lru:24;      // LRU 淘汰信息(高16位为时间,低8位为频率)
    int refcount;         // 引用计数
    void *ptr;            // 指向底层数据结构的指针
} robj;

3.2 逻辑类型与物理编码映射

逻辑类型 底层物理编码 切换条件(Redis 7.x 默认)
String int 值为 64 位整数
embstr 字符串长度 ≤ 44 字节
raw(SDS) 字符串长度 > 44 字节
List listpack 元素数 ≤ 128 且每个元素 ≤ 64 字节
quicklist 超出上述阈值
Hash listpack 键值对数 ≤ 128 且每个值 ≤ 64 字节
hashtable 超出上述阈值
Set intset 所有元素均为整数且元素数 ≤ 512
listpack 元素数 ≤ 128 且每个元素 ≤ 64 字节
hashtable 超出上述阈值
ZSet listpack 元素数 ≤ 128 且每个元素 ≤ 64 字节
skiplist + hashtable 超出上述阈值

以上阈值均可通过配置调整,如 list-max-listpack-size、set-max-listpack-entries 等。

3.3 核心底层数据结构详解

3.3.1 SDS(Simple Dynamic String)简单动态字符串

Redis 没有使用 C 语言原生字符串,而是自己实现了 SDS:

c 复制代码
struct sdshdr {
    int len;      // 已使用长度
    int free;     // 剩余可用长度
    char buf[];   // 字符数组
};

相比 C 字符串的优势:

特性 C 字符串 SDS
获取长度 O(N) 需遍历 O(1) 直接读取 len 字段
二进制安全 不安全(\0 为终止符) 安全(用 len 判断长度)
修改追加 可能溢出 自动扩容(先检查空间不足再分配)
内存分配 每次修改都重新分配 空间预分配 + 惰性释放,减少分配次数

扩容策略:

  • 修改后长度 < 1MB:分配 2 倍空间 + 1 字节
  • 修改后长度 ≥ 1MB:分配当前长度 + 1MB + 1 字节
3.3.2 跳表(Skip List)

跳表是 ZSet 底层的核心排序引擎,采用"空间换时间"思想,通过多层索引将链表 O(N) 的查找优化到 O(logN):

复制代码
Level 3:  1 ──────────────────────────────────> 9
Level 2:  1 ────────────> 4 ──────────────────> 9
Level 1:  1 ───> 3 ────> 4 ───> 6 ───────────> 9
Level 0:  1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9

查找元素 7 的路径:Level 3: 1 → 9(9>7,下降)→ Level 2: 1 → 4 → 9(9>7,下降)→ Level 1: 4 → 6 → 9(9>7,下降)→ Level 0: 6 → 7(找到!),只访问了 7 个节点。

c 复制代码
typedef struct zskiplistNode {
    sds ele;                         // 成员值
    double score;                    // 排序分数
    struct zskiplistNode *backward;  // 后退指针
    struct zskiplistLevel {
        struct zskiplistNode *forward;  // 前进指针
        unsigned long span;             // 跨度(用于排名计算)
    } level[];                       // 柔性数组,层数随机
} zskiplistNode;

为什么选跳表不选红黑树?

维度 跳表 红黑树
代码复杂度 约 300 行,实现简单 500+ 行,旋转操作复杂
范围查询 找到起点后沿链表遍历即可 需要中序遍历,实现复杂
内存开销 平均每个节点 1.33 个指针(p=0.25) 固定 3 个指针
并发友好 局部修改不影响全局结构 旋转操作影响全局
3.3.3 压缩列表(Ziplist / Listpack)

压缩列表是一段连续内存块,去除了指针开销,通过极致紧凑的编码方式节省内存:

复制代码
┌──────────┬──────────┬──────────┬──────────┬──────┐
│ zlbytes  │ zltail   │ zllen    │ entry1   │ ...  │
│ 4 bytes  │ 4 bytes  │ 2 bytes  │ 变长编码  │      │
└──────────┴──────────┴──────────┴──────────┴──────┘
  • zlbytes:整个列表占用的字节数
  • zltail:尾节点的偏移量,方便从尾部遍历
  • zllen:节点数量
  • entry:每个节点包含 prevlen(前一节点长度)+ encoding(编码方式)+ data(数据)

缺点 :连锁更新问题------当插入/删除节点导致前一节点长度变化时,后续所有节点的 prevlen 都需要重新编码,最坏情况 O(N)。Redis 7.0 引入 listpack 替代 ziplist,通过在每个节点中记录总长度来解决连锁更新问题。

3.3.4 整数集合(IntSet)

IntSet 是专门存储纯整数集合的紧凑结构:

c 复制代码
typedef struct intset {
    uint32_t encoding;  // 编码方式(INTSET_ENC_INT16/32/64)
    uint32_t length;    // 元素个数
    int8_t contents[];  // 有序整数数组
} intset;
  • 所有元素均为整数时才使用 intset
  • 元素按值排序存储,支持二分查找
  • 自动升级编码:当插入更大范围的整数时,从 int16 升级到 int32 或 int64

3.4 五种核心逻辑类型详解

3.4.1 String(字符串)

String 是最基础也是最灵活的类型,可以存储字符串、整数或浮点数:

bash 复制代码
# 基本操作
SET user:1:name "张三"           # 设置值
GET user:1:name                  # 获取值 → "张三"
INCR page:views                  # 原子递增(计数器)
SET session:token "abc" EX 3600  # 设置值并指定过期时间(秒)
SETNX lock:order "1"             # 仅当 key 不存在时设置(分布式锁)

典型应用:缓存热点数据、分布式锁、计数器、Session 共享、分布式 ID 生成

3.4.2 Hash(哈希)

Hash 适合存储对象,一个 key 映射多个 field-value:

bash 复制代码
# 基本操作
HSET user:1 name "张三" age 28 city "北京"  # 设置多个字段
HGET user:1 name                            # 获取单个字段 → "张三"
HMGET user:1 name age city                  # 获取多个字段
HINCRBY user:1 age 1                        # 字段值原子递增
HGETALL user:1                              # 获取所有字段和值

vs String 存 JSON:Hash 可以单独获取/修改某个字段,无需序列化/反序列化整个对象,内存更省(小对象用 listpack 编码时尤其明显)。

3.4.3 List(列表)

List 是有序的元素集合,底层用双向链表或 quicklist 实现:

bash 复制代码
# 基本操作
LPUSH queue:task "task1" "task2"   # 从左侧压入
RPUSH queue:task "task3"           # 从右侧压入
LPOP queue:task                    # 从左侧弹出 → "task2"
RPOP queue:task                    # 从右侧弹出
LRANGE queue:task 0 -1             # 获取所有元素
BLPOP queue:task 30                # 阻塞式弹出(超时30秒)

典型应用:消息队列、最新列表(Timeline)、文章列表、LRU 淘汰近似实现

3.4.4 Set(集合)

Set 是无序、不重复的元素集合,支持交并差集运算:

bash 复制代码
# 基本操作
SADD tags:article:1 "Redis" "缓存" "数据库"
SADD tags:article:2 "Redis" "高可用" "集群"
SMEMBERS tags:article:1                              # 获取所有成员
SISMEMBER tags:article:1 "Redis"                     # 判断是否存在 → 1
SINTER tags:article:1 tags:article:2                 # 交集 → {"Redis"}
SUNION tags:article:1 tags:article:2                 # 并集 → {"Redis","缓存","数据库","高可用","集群"}
SDIFF tags:article:1 tags:article:2                  # 差集 → {"缓存","数据库"}
SRANDMEMBER tags:article:1 2                         # 随机获取2个

典型应用:标签系统、共同好友、抽奖、去重、推荐(交集/并集运算)

3.4.5 ZSet(有序集合)

ZSet 在 Set 的基础上为每个元素关联一个 score,按 score 排序:

bash 复制代码
# 基本操作
ZADD leaderboard 100 "player1" 200 "player2" 150 "player3"
ZINCRBY leaderboard 50 "player1"                     # player1 分数+50 → 150
ZRANGE leaderboard 0 -1 WITHSCORES                   # 按分数升序
ZREVRANGE leaderboard 0 2 WITHSCORES                 # 按分数降序,取前3
ZRANGEBYSCORE leaderboard 100 200                    # 按分数范围查询
ZRANK leaderboard "player1"                          # 获取排名(从0开始)
ZCARD leaderboard                                    # 获取元素数量

典型应用:排行榜、延迟队列(score 为执行时间)、带权重的任务队列、滑动窗口限流


四、Redis 持久化机制

4.1 RDB 快照(Redis Database)

RDB 是 Redis 默认的持久化方式,将某一时刻的全量数据以二进制快照的形式保存到磁盘:

触发方式:

触发方式 配置/命令 说明
手动触发 SAVE / BGSAVE SAVE 阻塞主线程,BGSAVE fork 子进程
配置触发 save 900 1 900 秒内至少有 1 次写操作则触发
save 300 10 300 秒内至少有 10 次写操作则触发
save 60 10000 60 秒内至少有 10000 次写操作则触发
自动触发 主从复制全量同步时 主节点自动生成 RDB 发送给从节点

BGSAVE 工作流程:

复制代码
1. Redis 主进程 fork 一个子进程
2. 子进程将内存数据写入临时 RDB 文件
3. 期间主进程继续处理客户端请求(利用 COW 机制)
4. 子进程完成写入后,用临时文件替换旧 RDB 文件
5. 父进程收到子进程完成信号

Copy-On-Write(COW)机制:fork 时父子进程共享同一物理内存页,当主进程修改某个内存页时,操作系统会复制该页给主进程,子进程仍持有原始数据。这使得 BGSAVE 期间对内存的额外开销取决于写操作的多少。

优缺点:

  • ✅ 文件紧凑,恢复速度快(直接加载到内存)
  • ✅ 对 Redis 性能影响小(子进程处理,主进程不阻塞)
  • ❌ 可能丢失最后一次快照之后的数据
  • ❌ 数据量大时 fork 可能阻塞(fork 本身是阻塞操作)

4.2 AOF(Append Only File)

AOF 以日志形式记录每一条写命令,通过重放日志恢复数据:

三种刷盘策略:

策略 配置 数据安全性 性能
每次写入都 fsync always 最高,几乎不丢数据 最差
每秒 fsync 一次 everysec(默认) 较高,最多丢 1 秒数据 平衡
由操作系统决定 no 最低,丢失时长由 OS 策略决定 最好

AOF 重写(Rewrite):

AOF 文件会不断膨胀(记录了大量历史写命令),需要定期重写压缩:

复制代码
# 触发条件(默认配置)
auto-aof-rewrite-min-size 64mb     # AOF 文件至少 64MB 才触发重写
auto-aof-rewrite-percentage 100    # AOF 文件比上次重写后增长了 100% 时触发

# 重写流程
1. fork 子进程
2. 子进程根据当前内存数据生成新 AOF 文件(只写最终状态,忽略中间命令)
3. 期间新的写命令同时追加到旧 AOF 和重写缓冲区
4. 子进程完成新文件后,将缓冲区中的增量命令追加到新文件
5. 原子替换旧 AOF 文件

4.3 混合持久化(Redis 4.0+)

Redis 4.0 引入了混合持久化模式,将 RDB 和 AOF 结合:

复制代码
# 开启混合持久化
aof-use-rdb-preamble yes

# 文件格式
┌─────────────────┬─────────────────────┐
│  RDB 格式的全量数据 │  AOF 格式的增量命令   │
│  (最近一次快照)   │  (快照后的新写操作)  │
└─────────────────┴─────────────────────┘

优点:

  • 加载速度快(前半部分是 RDB 格式,直接加载到内存)
  • 数据丢失少(后半部分 AOF 记录了增量命令)
  • 兼顾了恢复速度和数据完整性

4.4 持久化选型建议

场景 推荐方案 理由
纯缓存,允许丢数据 关闭持久化或仅 RDB 性能最优,恢复快
一般缓存 RDB + AOF(everysec) 数据安全且性能平衡
数据不能丢失 AOF(always) 最高安全性,牺牲性能
生产推荐 混合持久化 加载快 + 数据完整

五、Redis 内存管理

5.1 内存模型

Redis 的内存占用由五个部分组成:

复制代码
┌──────────────────────────────────────────┐
│            Redis 进程总内存               │
│  ┌────────────┐  ┌────────────────────┐  │
│  │ 数据内存    │  │ 进程运行内存        │  │
│  │(键值对数据)│  │(代码/常量/堆栈)   │  │
│  └────────────┘  └────────────────────┘  │
│  ┌────────────────────────────────────┐  │
│  │         缓冲区内存                  │  │
│  │  ┌──────────┬──────────┬────────┐  │  │
│  │  │ 客户端    │ 复制     │ Pub/Sub│  │  │
│  │  │ 输入输出  │ 积压缓冲 │ 缓冲区  │  │  │
│  │  └──────────┴──────────┴────────┘  │  │
│  └────────────────────────────────────┘  │
│  ┌────────────────────────────────────┐  │
│  │ Lua 脚本内存(脚本+临时数据)       │  │
│  └────────────────────────────────────┘  │
└──────────────────────────────────────────┘

5.2 内存分配器

Redis 不直接向操作系统申请/释放内存,而是通过内存分配器统一管理:

分配器 特点
jemalloc(默认) 内存碎片率极低,将内存划分为不同大小的块,按申请大小匹配最合适的块
tcmalloc Google 开发,在高并发场景下性能略优
libc malloc 系统默认,碎片率较高

通过 INFO memory 命令可以查看内存指标,关键指标:

  • used_memory:Redis 分配器分配的内存总量
  • used_memory_rss:操作系统分配给 Redis 的物理内存
  • mem_fragmentation_ratio:rss / used_memory,碎片率指标,一般 1.0-1.5 为正常

5.3 过期删除策略

Redis 采用 惰性删除 + 定期删除 组合策略清理过期 key:

惰性删除:

  • 每次客户端访问某个 key 时,Redis 先检查该 key 是否已过期
  • 已过期则立即删除并返回空值
  • 优点:CPU 零额外开销
  • 缺点:过期 key 若长期不被访问,会永久占用内存

定期删除:

  • 定时任务 serverCron(默认每 100ms 执行一次)调用 activeExpireCycle 函数
  • 遍历过期键哈希槽抽样检查,单个槽位一轮最多 20 次采样
  • 抽样检测到过期 key 立即删除;样本过期占比≥25% 则持续循环抽样
  • 存在执行时长上限,默认最多运行 25ms,避免阻塞主线程
  • 优点:主动清理冷数据,平衡 CPU 与内存

5.4 内存淘汰策略

当已使用内存达到 maxmemory 阈值时,Redis 根据 maxmemory-policy 配置执行淘汰:

策略 淘汰范围 淘汰规则 适用场景
noeviction 不淘汰 写操作返回 OOM 错误 核心数据不可丢失
allkeys-lru 所有 key 淘汰最近最少使用的 key 纯缓存场景首选
allkeys-lfu 所有 key 淘汰访问频次最低的 key(Redis 4.0+) 纯缓存,有明确热点
allkeys-random 所有 key 随机淘汰 极少使用
volatile-lru 仅过期 key 淘汰过期 key 中最近最少使用的 混合存储(永久+临时)
volatile-lfu 仅过期 key 淘汰过期 key 中访问频次最低的 混合存储
volatile-random 仅过期 key 随机淘汰过期 key 过期 key 无明显热点
volatile-ttl 仅过期 key 淘汰剩余 TTL 最短的 key 短期临时缓存

LRU vs LFU:

  • LRU(最近最少使用):基于时间维度,淘汰最久未访问的 key。缺点:曾经的热点 key 长期不访问后仍占内存(缓存污染)
  • LFU(最不经常使用):基于频次维度,利用 redisObject 的 24 位 lru 字段(高 16 位记录时间,低 8 位用 Morris 概率计数器记录频次),配合时间衰减机制(lfu-decay-time)

生产选型建议:

  • 纯缓存 → allkeys-lfu 或 allkeys-lru
  • 混合存储 → volatile-lru 或 volatile-lfu
  • 核心数据不丢 → noeviction(配合手动扩容)

六、Redis 事务与高级特性

6.1 事务机制

Redis 事务通过 MULTI/EXEC 实现:

bash 复制代码
MULTI                    # 开启事务
SET account:1:balance 100
INCRBY account:1:balance -50
SET account:2:balance 50
INCRBY account:2:balance 50
EXEC                     # 执行事务(按顺序原子执行)

特性:

  • 命令入队时不做执行,只返回 QUEUED
  • EXEC 时按顺序依次执行所有命令
  • 不支持回滚------如果某条命令执行出错,后续命令仍然执行
  • 通过 WATCH 实现乐观锁:
bash 复制代码
WATCH account:1:balance      # 监视 key
MULTI
# ... 检查余额并扣款
EXEC                         # 如果 WATCH 的 key 被其他客户端修改,EXEC 返回 nil

6.2 Pipeline(管道)

Pipeline 将多个命令打包一次性发送到服务端,减少网络 RTT:

bash 复制代码
# 不用 Pipeline(4次网络往返)
SET key1 value1
SET key2 value2
SET key3 value3
GET key1

# 使用 Pipeline(1次网络往返)
Pipeline:
  SET key1 value1
  SET key2 value2
  SET key3 value3
  GET key1

性能差异:单条命令 0.1ms RTT,1000 条命令不用 Pipeline 需要 100ms,使用 Pipeline 约 10ms。

6.3 Lua 脚本

Redis 支持执行 Lua 脚本,保证脚本内命令的原子性:

bash 复制代码
EVAL "
local current = redis.call('GET', KEYS[1])
if current == false then
    redis.call('SET', KEYS[1], ARGV[1])
    return 1
end
return 0
" 1 mykey myvalue

Lua 脚本的原子性保证:整个脚本作为一个命令执行,执行期间不会被其他命令插入。适合实现分布式锁、限流器等需要原子性的复杂操作。

6.4 Pub/Sub 发布订阅

bash 复制代码
# 订阅频道
SUBSCRIBE news:sports news:tech

# 发布消息
PUBLISH news:sports "国足2:1胜出"

# 模式订阅(支持通配符)
PSUBSCRIBE news:*

Stream(Redis 5.0+) 是对 Pub/Sub 的升级,支持消息持久化、消费者组和 ACK:

bash 复制代码
# 创建消息
XADD mystream * sensor_id 1234 temperature 19.8

# 消费者组
XGROUP CREATE mystream mygroup $ MKSTREAM
XREADGROUP GROUP mygroup consumer1 COUNT 10 BLOCK 5000 STREAMS mystream >
XACK mystream mygroup 1526985685-0

七、Redis 高可用架构

7.1 主从复制

核心原理:

复制代码
┌──────────┐    全量同步(首次)    ┌──────────┐
│  Master   │ ──────────────────> │  Slave1   │
│  (主节点)  │    增量同步(后续)    │  (从节点)  │
│           │ ──────────────────> │           │
│           │                      └──────────┘
│           │    全量/增量同步      ┌──────────┐
│           │ ──────────────────> │  Slave2   │
└──────────┘                      │  (从节点)  │
                                  └──────────┘

全量同步(首次连接或断连过久):

  1. Slave 发送 PSYNC ? -1
  2. Master 执行 BGSAVE 生成 RDB 快照
  3. Master 将 RDB 发送给 Slave
  4. Slave 加载 RDB 到内存
  5. Master 将期间的增量命令发送给 Slave 执行

增量同步(正常情况):

  1. Master 维护一个 replication backlog(复制积压缓冲区,默认 1MB)
  2. Slave 发送 PSYNC <runid> <offset>
  3. Master 根据 offset 从 backlog 中发送增量数据

7.2 哨兵模式(Sentinel)

哨兵是主从复制的升级方案,提供自动故障转移:

核心功能:

  • 监控:持续检测 Master 和 Slave 是否正常运行
  • 通知:当被监控的实例出现问题时,通过 Pub/Sub 通知管理员
  • 自动故障转移:Master 不可用时,自动将某个 Slave 提升为新的 Master
  • 配置中心:客户端连接 Sentinel 获取当前 Master 地址,故障转移后自动更新

故障判定流程:

  1. Sentinel 每秒向所有实例发送 PING
  2. 超过 down-after-milliseconds 未响应 → 标记为主观下线(SDOWN)
  3. 多个 Sentinel 都认为 Master 主观下线 → 标记为客观下线(ODOWN)
  4. Sentinel 选举一个 Leader 执行故障转移
  5. Leader 选择一个 Slave 提升为新 Master(优先级:replica-priority 最高、复制偏移量最大、runid 最小)
  6. 其他 Slave 改为复制新 Master
  7. 通知客户端新 Master 地址
bash 复制代码
# Sentinel 配置示例
sentinel monitor mymaster 127.0.0.1 6379 2     # 监控主节点,2个sentinel同意即故障转移
sentinel down-after-milliseconds mymaster 5000  # 5秒无响应视为下线
sentinel parallel-syncs mymaster 1              # 故障转移后一次只同步一个从节点
sentinel failover-timeout mymaster 60000        # 故障转移超时时间

7.3 Redis Cluster(集群)

Redis Cluster 是 Redis 官方的分布式方案(Redis 3.0+),提供数据分片、高可用和水平扩展:

核心设计:

  • 数据分片:使用哈希槽(Hash Slot)机制,共 16384 个槽
  • 槽分配 :CRC16(key) % 16384 决定 key 属于哪个槽
  • 节点分配:每个 Master 负责一部分槽,Slave 作为对应 Master 的备份

架构示例(3 主 3 从):

复制代码
Master A: slot 0-5460      ←→    Slave A': 备份 Master A
Master B: slot 5461-10922  ←→    Slave B': 备份 Master B
Master C: slot 10923-16383 ←→    Slave C': 备份 Master C

关键特性:

特性 说明
去中心化 所有节点通过 Gossip 协议通信,无中心代理
客户端重定向 访问错误的节点时返回 MOVED 重定向到正确的节点
在线扩缩容 通过迁移槽实现节点的添加和移除,服务不中断
故障转移 集群中多数 Master 认为某 Master 不可达时,自动提升其 Slave
异步复制 主从之间异步复制,极端情况下可能丢数据

MOVED 与 ASK:

  • MOVED slot ip:port:槽已永久迁移,客户端更新本地路由表
  • ASK slot ip:port:槽正在迁移中(临时状态),客户端本次重定向,不更新路由表

Hash Tag :通过 {} 强制相关 key 落在同一槽:

bash 复制代码
# 以下两个 key 会落在同一个槽(因为 hash tag 都是 user:1)
SET {user:1}:name "张三"
SET {user:1}:age 28

八、Redis 生产实践

8.1 缓存三大问题

问题 描述 解决方案
缓存穿透 查询不存在的数据,每次都穿透到 DB 布隆过滤器;空值缓存(设短 TTL)
缓存击穿 热点 key 过期瞬间,大量请求打到 DB 互斥锁(SETNX);热点 key 永不过期;逻辑过期
缓存雪崩 大量 key 同时过期或 Redis 宕机 TTL 加随机值;多级缓存;集群高可用

8.2 分布式锁实现

bash 复制代码
# 加锁(原子操作)
SET lock:order:123 uuid NX PX 30000

# 解锁(Lua 脚本保证原子性)
EVAL "
if redis.call('GET', KEYS[1]) == ARGV[1] then
    return redis.call('DEL', KEYS[1])
else
    return 0
end
" 1 lock:order:123 uuid

生产建议:单机锁用 Redis 即可;多节点强一致性场景推荐使用 RedLock 或 ZooKeeper。

8.3 Redis 与其他组件的对比

维度 Redis Memcached MongoDB
数据结构 丰富(String/Hash/List/Set/ZSet/Stream) 简单 KV 文档型(BSON)
持久化 RDB + AOF 不支持 原生支持
线程模型 单线程执行(6.0+ 多线程IO) 多线程 多线程
集群 Cluster(原生分片) 客户端分片 原生分片
内存管理 精细(jemalloc + 多种淘汰策略) 简单 slab 分配 WiredTiger 引擎
事务 MULTI/EXEC(不支持回滚) 不支持 多文档事务(4.0+)
适用场景 缓存/队列/排行榜/分布式锁 简单缓存 文档存储/内容管理

8.4 关键配置调优

bash 复制代码
# 内存配置
maxmemory 8gb                    # 最大内存(建议预留 30% 给系统)
maxmemory-policy allkeys-lfu     # 淘汰策略

# 持久化配置
save 900 1                       # RDB 触发条件
save 300 10
save 60 10000
aof-use-rdb-preamble yes         # 混合持久化
auto-aof-rewrite-percentage 100  # AOF 重写触发

# 网络配置
tcp-backlog 511                  # TCP 全连接队列
timeout 300                      # 空闲连接超时(0为不超时)
tcp-keepalive 300                # TCP 保活时间

# 安全配置
requirepass your-strong-password # 设置密码
rename-command FLUSHALL ""       # 禁用危险命令
rename-command CONFIG ""

# 性能配置
hz 10                            # serverCron 执行频率(默认10,高负载可调至100)
slowlog-log-slower-than 10000    # 慢查询阈值(微秒)
slowlog-max-len 128              # 慢查询日志最大条数

九、 安装配置

9.1 下载安装

手动创建 SCLo-scl 仓库文件

bash 复制代码
sudo vi /etc/yum.repos.d/CentOS-SCLo-scl.repo

输入以下内容

bash 复制代码
[centos-sclo-sclo]
name=CentOS-7 - SCLo sclo
baseurl=https://mirrors.aliyun.com/centos/7/sclo/$basearch/sclo/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

[centos-sclo-sclo-source]
name=CentOS-7 - SCLo sclo Sources
baseurl=https://mirrors.aliyun.com/centos/7/sclo/Source/sclo/
gpgcheck=1
enabled=0
gpgkey=https://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

[centos-sclo-sclo-debuginfo]
name=CentOS-7 - SCLo sclo Debug
baseurl=https://mirrors.aliyun.com/centos/7/sclo/$basearch/debug/sclo/
gpgcheck=1
enabled=0
gpgkey=https://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

手动创建 SCLo-rh 仓库文件(devtoolset 依赖此库)

bash 复制代码
sudo vi /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo

输入以下内容

bash 复制代码
[centos-sclo-rh]
name=CentOS-7 - SCLo rh
baseurl=https://mirrors.aliyun.com/centos/7/sclo/$basearch/rh/
gpgcheck=1
enabled=1
gpgkey=https://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

[centos-sclo-rh-source]
name=CentOS-7 - SCLo rh Sources
baseurl=https://mirrors.aliyun.com/centos/7/sclo/Source/rh/
gpgcheck=1
enabled=0
gpgkey=https://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

[centos-sclo-rh-debuginfo]
name=CentOS-7 - SCLo rh Debug
baseurl=https://mirrors.aliyun.com/centos/7/sclo/$basearch/debug/rh/
gpgcheck=1
enabled=0
gpgkey=https://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7

重建 yum 缓存

bash 复制代码
sudo yum clean all
sudo yum makecache

安装C语言编译环境

bash 复制代码
sudo yum install centos-release-scl scl-utils-build 
sudo yum install -y --nogpgcheck devtoolset-8-toolchain
# 注意: 执行此命令会自动切换到 root 用户
sudo  scl enable devtoolset-8 bash

测试 gcc 版本

bash 复制代码
gcc --version

下载地址:https://download.redis.io/releases/

下载后上传到 hadoop1 的 /opt/software 目录下,并用 tar 命令解压

进入解压后的 redis 目录使用 make 进行编译

编译完成后继续执行 make install 进行安装

安装目录:/usr/local/bin

9.2 后台启动配置

一份redis.conf到 hadoop 家目录下

bash 复制代码
cp /opt/module/redis-6.0.8/redis.conf ~/my_redis.conf

修改 vim ~/my_redis.conf 文件

bash 复制代码
daemonize yes
# bind 127.0.0.1  注释掉这个配置
protected-mode no

9.3 启动和关闭

bash 复制代码
redis-server ~/my_redis.conf   # 启动
redis-cli					   # 客户端访问
redis-cli shutdown             # 关闭

十、关键要点总结

  1. 纯内存 + 单线程执行 是 Redis 高性能的基石,配合 IO 多路复用实现单线程万级并发
  2. 两层架构设计 是 Redis 最精妙的设计之一------对外暴露 5 种逻辑类型,底层自动选择最优物理编码(redisObject 桥接 type 和 encoding)
  3. 跳表 是 ZSet 的排序引擎,相比红黑树实现更简单、范围查询更高效、内存更友好
  4. 持久化选型 直接影响数据安全性和性能------生产推荐混合持久化(RDB + AOF 增量),兼顾恢复速度和数据完整性
  5. 过期清理 采用惰性删除 + 定期删除组合策略,是 CPU 和内存之间的折中设计
  6. 内存淘汰 提供 8 种策略,纯缓存场景推荐 allkeys-lfu 或 allkeys-lru
  7. 高可用演进路径:主从复制(读写分离)→ 哨兵模式(自动故障转移)→ Cluster 集群(水平扩展 + 去中心化分片)
  8. 生产实践 需重点关注缓存穿透/击穿/雪崩的预防、分布式锁的正确实现、危险命令的禁用、以及慢查询的持续监控
相关推荐
动恰客流统计2 小时前
景区客流统计怎么做?兼顾管控与运营的实施方案解析
大数据·前端·人工智能
weixin_443883012 小时前
合规整改倒计时:高等级签名证书的应用场景
大数据·人工智能·法大大·法大大电子签·电子合同
笨小孩@GF 知行合一3 小时前
易语言-高级应用
数据库·编程·易语言·中文编程
KaiwuDB3 小时前
KaiwuDB 运维实战04:DRBD + KaiwuDB——物联网场景下的低成本数据库高可用方案
运维·数据库·物联网·时序数据库·kaiwudb·aiot·多模数据库
hweiyu004 小时前
Redis命令:HMGET
redis·缓存
dozenyaoyida4 小时前
AI与大模型新闻日报 | 2026-09-30
大数据·人工智能·大模型·新闻
辻弋2014 小时前
【无标题】
大数据·服务器·前端·搜索引擎·开源软件
衡石科技5 小时前
让问数更自然:衡石 Data Agent 实践
大数据·bi·数据权限·data agent·自然语言问数
实验室管理云平台5 小时前
应用盛元广通的SPF系统后,养殖场死亡率降低约20%
大数据·人工智能