一、Redis 数据结构与过期机制

1.1 redisObject 结构

Redis 使用 redisObject 存储 key-value 中的 value,过期信息存储在 redisObject 中:

redisObject {
    type          // 数据类型:string、list、hash、set、zset
    encoding      // 编码方式:int、embstr、raw、linkedlist、ziplist
    ptr           // 指向实际数据结构的指针
    lru          // LRU 时间戳(用于淘汰策略)
    refcount      // 引用计数(用于对象共享)
}

过期命令存储在 redisObject 中,整个数据结构存储在 ptr 指针指向的内存中。

1.2 过期机制(Expire)

Redis 为每个 key 提供过期时间设置:

命令 说明
EXPIRE key seconds 设置多少秒后过期
PEXPIRE key milliseconds 设置多少毫秒后过期
TTL key 查看剩余生存时间(秒)
PTTL key 查看剩余生存时间(毫秒)

核心特性

  • EXPIRE 只针对最外层 key,不对 list 中单个 value 设置过期
  • 只对整个链表进行淘汰,无法单独淘汰 list 中的某个元素
  • 过期时间存储在 redisObject 的 lru 字段

二、Redis 淘汰策略

2.1 内存上限配置(maxmemory)

maxmemory 48GB   # 机器 96GB 时设置一半

为什么是一半?

  • RedisDB 散列表扩容时会进行翻倍
  • 渐进式迁移会占据额外内存
  • RDB 持久化 fork 子进程会复制整个内存(写时复制)

⚠️ 进行 RDB 持久化时,物理内存必须是 maxmemory 的 2 倍以上

2.2 淘汰策略(maxmemory-policy)

当内存达到上限时,继续插入数据所采取的策略:

策略 说明
noeviction 默认策略,不淘汰任何数据,只返回错误
allkeys-lru 所有 key 中淘汰最近最少使用
allkeys-lfu 所有 key 中淘汰访问次数最少
allkeys-random 所有 key 中随机淘汰
volatile-lru 已设置过期时间的 key 中淘汰 LRU
volatile-lfu 已设置过期时间的 key 中淘汰 LFU
volatile-random 已设置过期时间的 key 中随机淘汰
volatile-ttl 已设置过期时间的 key 中淘汰 TTL 最短的

volatile:易变的,指已设置 expire 的 key。

淘汰策略选择

  • 有明显热点数据 → allkeys-lru
  • 追求数据均衡 → allkeys-random
  • 只淘汰带过期时间的 → volatile-*

三、Redis 持久化机制

3.1 内存数据的问题

内存数据存放在 RAM 中,宕机时数据全部丢失。解决思路:把内存数据持久化到磁盘

3.2 Linux 文件写入流程

用户空间                         内核空间
+--------+                     +--------+
| buffer |  ← fwrite() ←       | page   |
| (用户态)|                     | cache  |
+--------+                     |(内核态) |
     ↓ fflush()                ↓ fsync()
     ↓ write()                 ↓ 落盘
     ↓                         ↓
     +------------------------→+--------+
                               |  磁盘   |
                               +--------+
场景 数据是否丢失
机器断电 只有 fsync 后的数据保住
进程崩溃 buffer 中未刷到 page cache 的数据丢失

3.3 写时复制(Copy-On-Write)

fork() 创建子进程时,父子进程页表指向同一份物理内存(只读状态):

fork() → 父子进程页表指向同一物理内存(只读)
     ↓ 父进程写数据
     ↓ 触发写保护中断
     ↓ 复制物理页,重新建立映射
     ↓ 改为可读可写

写时复制的应用:fork 进程那一刻,相当于给父进程内存打了一个快照。RDB 持久化正是利用这一机制。

3.4 RDB 持久化

原理:fork 子进程,遍历内存中的哈希表直接将数据写入磁盘。

优点:
✅ 文件小,恢复速度快
✅ fork 子进程,不阻塞主进程

缺点:
❌ 可能丢失最后一次快照后的数据(最多一分钟)
❌ fork 时复制内存,资源消耗大

配置

save 900 1      # 900秒内至少1次写操作则触发bgsave
save 300 10     # 300秒内至少10次写操作则触发bgsave
save 60 10000   # 60秒内至少10000次写操作则触发bgsave

3.5 AOF 持久化

原理:将每个写命令追加到文件末尾,是顺序磁盘 I/O,约等于内存随机 I/O 的访问速度。

优点:
✅ 数据可靠性强(最多丢失1秒的数据)
✅ 持久化代价低

缺点:
❌ 文件较大(记录每个 key 的多次变更)
❌ 数据恢复速度慢

落盘策略

策略 说明
always 每次写命令立即落盘(最安全但最慢)
everysec 每秒调用 fsync 落盘(默认,推荐)
no 只刷到 page cache,由系统决定何时落盘

AOF 重写(Rewrite)

  • AOF 记录每个 key 的三次变更会冗余三个记录
  • AOF 重写通过 fork 进程,根据当前内存状态生成新文件
  • 避免同一 key 的历史冗余数据
  • RDB 和 AOF 重写都是基于内存状态,没有冗余数据

3.6 RDB-AOF 混合持久化

开启方式

appendonly yes                    # 开启AOF
aof-use-rdb-preamble yes         # 开启混合持久化
auto-aof-rewrite-percentage 100  # 允许AOF重写
save ""                           # 关闭纯RDB

工作流程

  1. fork 子进程,根据当前内存生成 RDB 格式数据
  2. 持久化结束后,将 RDB 数据附加到 AOF 文件末尾
  3. 持久化期间的新写操作,继续记录到 AOF buffer

两种持久化方式对比

特性 AOF RDB
数据可靠性 丢失较少 丢失较多
文件大小
恢复速度
持久化代价

四、Redis 主从同步

4.1 为什么需要主从同步?

防止主 Redis 所在的磁盘损坏,造成数据永久丢失。主从同步实现数据可靠性

4.2 主从同步命令

redis-server --replicaof <master-ip> <master-port>

架构优势:从和主连接,只需更换从节点,可以大大提高灵活性。

4.3 复制原理

Redis 主从同步依赖两个核心概念:

概念 作用
runid Redis 实例唯一标识,判断是否是同一主从关系
offset 复制偏移量,记录同步进度
环形缓冲区 存放近期写命令,支持增量同步

同步流程

Replica 连接 Master
       ↓
交换 runid,判断是否需要全量同步
       ↓
比较 offset 是否在环形缓冲区中
       ├── 在 → 增量同步
       └── 不在 → 全量同步(RDB)

4.4 异步复制

优点:高效,不阻塞主进程
缺点:可能丢失数据(网络抖动时)

优化方案:采用分布式共识思想,半数以上副本同步成功才返回成功。


五、Redis 高可用方案

5.1 哨兵模式(Sentinel)

问题:主数据库宕机了,服务器就不能正常运行了。

解决方案:通过哨兵集群实现主从切换。

+--------+         +-------------+
| Master | ← 监控  | Sentinel-1  |
+--------+         +-------------+
|                   | Sentinel-2  |  ← 哨兵集群
+--------+         +-------------+
| Slave  |         | Sentinel-3  |
+--------+         +-------------+

工作流程

  1. 哨兵集群监控主节点健康状态
  2. 主节点宕机时,自动选举新主节点
  3. 客户端从哨兵获取新主节点地址
  4. 其他从节点切换到新主节点

可用性定义:read or write always success,在合理时间内返回合理的答案。

致命缺点:不能进行横向扩展,只保证主 Redis 可用。

5.2 Cluster 集群模式(推荐)

核心思想:数据分片存储在多个节点,每个节点可带从节点,去中心化架构

        +-------+
        | Slot  |
        | 0-5460|
        +-------+
            ↑
    +-------------+     +-------------+
    | Redis-1    |←复制 | Redis-4     |
    | (Master)   |     | (Slave)     |
    +-------------+     +-------------+

        +-------+           +-------+
        | Slot  |           | Slot  |
        |5461-10922|       |10923-16383|
        +-------+           +-------+
            ↑                   ↑
    +-------------+     +-------------+
    | Redis-2    |←复制 | Redis-5     |
    | (Master)   |     | (Slave)     |
    +-------------+     +-------------+

        +-------+           +-------+
        | Slot  |           | Slot  |
        |10924-16383|       |0-5460|
        +-------+           +-------+
            ↑                   ↑
    +-------------+     +-------------+
    | Redis-3    |←复制 | Redis-6     |
    | (Master)   |     | (Slave)     |
    +-------------+     +-------------+

5.3 槽位分片原理

  • 整个集群有 2^14 = 16384 个槽位
  • 每个 key 通过 CRC16(key) % 16384 决定槽位
  • 每个 Redis 节点负责一部分槽位
  • 引入虚拟节点增大样本数,确保 key 负载均衡
  • 落在同一虚拟节点的 key 必定落在同一个物理节点

5.4 数据迁移

当某个节点内存不足时,可以将槽位中的数据迁移到另一个节点,迁移过程对客户端透明。

5.5 集群搭建简述

# 创建6个目录
mkdir -p redis-cluster/{7000,7001,7002,7003,7004,7005}

# 每个配置文件关键配置
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

# 启动6个节点后,创建集群
redis-cli --cluster create 127.0.0.1:7000-7005 \
  --cluster-replicas 1

特点

  • 可从任意节点进入整个集群
  • 一个节点宕机,其他节点自动选举新主节点
  • 只需知道一个节点地址即可读写数据

六、大 Key 对持久化的影响

问题 影响
fsync 压力大 AOF 追加频繁,磁盘 I/O 成为瓶颈
fork 时间长 大内存 fork 耗时长,可能阻塞主进程
内存膨胀 RDB/AOF 文件过大,恢复时间长

优化建议

  • 单个 String value 不超过 10KB
  • List/Hash/Set 等结构控制元素数量
  • 定期拆分大 key

总结

┌─────────────────────────────────────────────────┐
│              Redis 高可用架构演进                │
├─────────────────────────────────────────────────┤
│                                                 │
│  主从复制 ───→ 数据备份                          │
│      ↓                                          │
│  哨兵模式 ───→ 主从自动切换(不能横向扩展)       │
│      ↓                                          │
│  Cluster ───→ 去中心化分片 + 自动故障转移       │
│                                                 │
│  持久化(RDB / AOF / 混合)                      │
│      ↓                                          │
│  淘汰策略(LRU / LFU / Random / volatile)       │
│                                                 │
└─────────────────────────────────────────────────┘

技术选型

  • 单机主从 + 哨兵 → 简单场景
  • Cluster 模式 → 生产环境推荐
  • 数据安全优先 → AOF + RDB 混合持久化

根据零声教育教学写作https://github.com/0voice

Logo

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

更多推荐