Redis 主从同步与对象模型
一、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
工作流程:
- fork 子进程,根据当前内存生成 RDB 格式数据
- 持久化结束后,将 RDB 数据附加到 AOF 文件末尾
- 持久化期间的新写操作,继续记录到 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 |
+--------+ +-------------+
工作流程:
- 哨兵集群监控主节点健康状态
- 主节点宕机时,自动选举新主节点
- 客户端从哨兵获取新主节点地址
- 其他从节点切换到新主节点
可用性定义: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
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)