Redis持久化RDB与AOF
Redis 的五种数据结构各有特色,用对了才能发挥它的优势。很多人只用到了 String 和 Hash,却不知道 List、Set、ZSet 在特定场景下更合适。本文从应用场景出发,讲什么时候用什么类型。
一、持久化概述
#
1.1 为什么需要持久化
- Redis数据存储在内存,进程退出数据丢失
- 持久化将内存数据保存到磁盘
- 用于重启恢复、备份、复制同步
#
1.2 两种持久化方式
| 方式 |
说明 |
优点 |
缺点 |
| RDB |
快照,保存某一时刻的数据 |
文件紧凑,恢复速度快 |
可能丢数据 |
| AOF |
记录写操作日志 |
数据安全性高 |
文件大,恢复慢 |
二、RDB持久化
#
2.1 什么是RDB
RDB(Redis Database)是将某一时刻的内存数据生成快照保存到磁盘:
内存数据 ──fork──> 子进程 ──写入──> dump.rdb
|
#
2.2 触发方式
手动触发:
自动触发(配置文件):
# redis.conf save 900 1 # 900秒内至少有1个key变化 save 300 10 # 300秒内至少有10个key变化 save 60 10000 # 60秒内至少有10000个key变化
|
其他触发:
- 执行FLUSHALL/FLUSHDB(如果配置了save)
- 主从复制时,从节点发起同步
- 执行SHUTDOWN且没有开启AOF
#
2.3 BGSAVE的fork机制
主进程 子进程 │ │ ├── fork() ─────────────>│ │ (瞬间完成) │ │ │ │ 继续服务客户端 ├── 扫描内存 │ │ 写入RDB文件 │ │ │ <─────────────────────┤ │ 子进程退出 │ 写入完成
|
fork的过程:
pid_t childpid = fork(); if (childpid == 0) { rdbSave(filename); exit(0); } else { }
|
Copy-On-Write(写时复制):
fork后内存状态:
父进程页表 子进程页表 ┌─────────┐ ┌─────────┐ │ 虚拟页1 │──┬──> │ 虚拟页1 │──┬──> 物理页1 │ 虚拟页2 │──┼──> │ 虚拟页2 │──┼──> 物理页2 └─────────┘ │ └─────────┘ │ └───────────────────┘ 共享物理内存(只读)
父进程修改数据后:
父进程页表 子进程页表 ┌─────────┐ ┌─────────┐ │ 虚拟页1 │──┬──> │ 虚拟页1 │──┬──> 物理页1(旧) │ 虚拟页2 │──┼──> │ 虚拟页2 │──┘ └─────────┘ │ └─────────┘ └──> 物理页2'(新,复制) 父进程修改虚拟页2时: 1. 复制物理页2到新页2' 2. 父进程页表指向新页2' 3. 子进程页表仍指向旧页2
|
COW的优势:
- fork瞬间完成,不拷贝数据
- 只有修改的页才会复制
- 子进程看到fork时的数据快照
#
2.4 RDB文件格式
RDB文件结构: ┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐ │ REDIS │ VERSION │ SELECT │ DB DATA│ SELECT │ EOF │ │ magic │ 0009 │ DB 0 │ │ DB 1 │ + CRC │ │ 5字节 │ 4字节 │ │ │ │ │ └─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
DB DATA格式: ┌─────────┬─────────┬─────────┬─────────┐ │ EXPIRE │ TYPE │ KEY │ VALUE │ │ (可选) │ │ │ │ └─────────┴─────────┴─────────┴─────────┘
|
#
2.5 RDB配置
# redis.conf
# RDB文件名 dbfilename dump.rdb
# RDB文件保存目录 dir /var/lib/redis
# 停止写入如果RDB失败 stop-writes-on-bgsave-error yes
# RDB文件压缩 rdbcompression yes
# RDB文件校验 rdbchecksum yes
|
#
2.6 RDB的优缺点
优点:
- 文件紧凑,适合备份和灾难恢复
- 恢复速度快(直接加载到内存)
- 对性能影响小(子进程处理)
缺点:
- 可能丢失最后一次save后的数据
- fork时内存占用可能翻倍(COW期间)
三、AOF持久化
#
3.1 什么是AOF
AOF(Append Only File)记录每个写操作命令:
客户端: SET key value Redis: 将命令追加到AOF文件
AOF文件内容: *3\r $3\r SET\r $3\r key\r $5\r value\r
|
#
3.2 AOF持久化流程
客户端写命令 │ ▼ ┌─────────────┐ │ AOF缓冲区 │ ← 先写入内存缓冲区 └──────┬──────┘ │ ├── sync策略 ──> │ ▼ ┌─────────────┐ │ AOF文件 │ ← 同步到磁盘 └─────────────┘
|
#
3.3 AOF同步策略
# redis.conf
# 每次写入都fsync(最安全,最慢) appendfsync always
# 每秒fsync一次(推荐,平衡安全性和性能) appendfsync everysec
# 由操作系统决定(最快,最不安全) appendfsync no
|
| 模式 |
说明 |
数据丢失 |
性能 |
| always |
每次写入fsync |
0 |
低 |
| everysec |
每秒fsync |
<=1秒 |
中 |
| no |
OS决定 |
可能很多 |
高 |
推荐:everysec(默认)
#
3.4 AOF重写
为什么需要重写:
AOF文件不断增长: SET key value1 SET key value2 SET key value3 ...
同一个key多次修改,只有最后一次有效 需要压缩只保留最终状态
|
重写原理:
┌─────────────┐ fork ┌─────────────┐ │ 旧AOF文件 │ ──────────> │ 子进程 │ │ (很大) │ │ 读取内存 │ └─────────────┘ │ 生成新AOF │ └──────┬──────┘ │ ▼ ┌─────────────┐ │ 新AOF文件 │ │ (紧凑) │ └─────────────┘
重写期间,新的写命令同时写入: 1. 旧AOF文件(继续追加) 2. AOF重写缓冲区(子进程结束后追加到新文件)
|
触发方式:
# 自动触发 auto-aof-rewrite-percentage 100 # AOF文件增长100% auto-aof-rewrite-min-size 64mb # 最小64MB才触发
# 手动触发 BGREWRITEAOF
|
#
3.5 AOF配置
# 开启AOF appendonly yes
# AOF文件名 appendfilename "appendonly.aof"
# 同步策略 appendfsync everysec
# 重写时是否禁用fsync(减少IO压力) no-appendfsync-on-rewrite no
# 自动重写配置 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
# 加载AOF时是否忽略末尾错误(如AOF被截断) aof-load-truncated yes
# 使用RDB前缀加速AOF恢复(Redis 4.0+) aof-use-rdb-preamble yes
|
#
3.6 AOF的优缺点
优点:
- 数据安全性高(everysec只丢1秒)
- AOF文件可读,可手动修复
- 支持重写压缩
缺点:
- 文件比RDB大
- 恢复速度比RDB慢
- 对性能有一定影响
四、RDB与AOF对比
| 特性 |
RDB |
AOF |
| 文件大小 |
小(压缩) |
大(文本命令) |
| 恢复速度 |
快 |
慢 |
| 数据安全 |
可能丢数据 |
只丢1秒(everysec) |
| 对性能影响 |
fork时短暂影响 |
持续影响(fsync) |
| 可读性 |
二进制,不可读 |
文本,可读可修复 |
| 适合场景 |
备份、灾难恢复 |
数据安全要求高 |
五、持久化方案选择
#
5.1 推荐方案
方案一:RDB + AOF(推荐)
# 同时开启RDB和AOF save 900 1 save 300 10 save 60 10000
appendonly yes appendfsync everysec aof-use-rdb-preamble yes
|
- RDB用于快速恢复和备份
- AOF保证数据安全
- Redis 4.0+支持混合持久化
方案二:仅RDB
方案三:仅AOF
save "" appendonly yes appendfsync everysec
|
#
5.2 混合持久化(Redis 4.0+)
AOF文件格式:
┌─────────────────┬─────────────────────┐ │ RDB格式前缀 │ AOF格式追加命令 │ │ (全量数据) │ (增量命令) │ └─────────────────┴─────────────────────┘
恢复时: 1. 先加载RDB前缀(快速) 2. 再执行AOF命令(精确)
|
优势:
六、恢复流程
#
6.1 启动时恢复
Redis启动 │ ├── AOF开启? │ ├── 是 → 加载AOF文件 │ │ └── 成功 → 启动完成 │ │ └── 失败 → 报错退出(可配置截断加载) │ └── 否 → 加载RDB文件 │ └── 成功 → 启动完成 │ └── 失败 → 空数据启动 │ └── 如果AOF和RDB都存在,优先AOF
|
#
6.2 灾难恢复
redis-cli SHUTDOWN
cp appendonly.aof appendonly.aof.bak cp dump.rdb dump.rdb.bak
cp /backup/dump.rdb /var/lib/redis/
cp /backup/appendonly.aof /var/lib/redis/
redis-server /etc/redis/redis.conf
redis-cli INFO keyspace
|
七、常见问题
#
7.1 fork耗时过长
现象:BGSAVE或AOF重写时,Redis卡顿
原因:
解决:
redis-cli INFO stats | grep latest_fork_usec
echo never > /sys/kernel/mm/transparent_hugepage/enabled
|
#
7.2 AOF文件过大
现象:AOF文件持续增长
解决:
redis-cli BGREWRITEAOF
redis-cli CONFIG GET auto-aof-rewrite*
|
#
7.3 持久化失败
现象:Redis日志报错 “Can’t save in background”
原因:
- 内存不足(COW需要额外内存)
- 磁盘空间不足
- 权限问题
解决:
df -h
free -h
ls -la /var/lib/redis/
|
八、总结
| 场景 |
推荐方案 |
| 数据安全要求高 |
RDB + AOF(everysec) |
| 纯缓存场景 |
仅RDB或关闭持久化 |
| 快速恢复 |
混合持久化(4.0+) |
| 大数据量 |
RDB为主,定期备份 |
持久化配置的核心原则:
- 同时开启RDB和AOF(生产环境)
- AOF使用everysec模式
- 定期验证备份可用性
- 监控fork耗时和持久化状态
- 确保足够的磁盘空间和内存
核心要点
String:简单的键值对,适合缓存、计数器
Hash:存储对象属性,适合用户信息、配置
List:有序列表,适合消息队列、最新列表
Set:无序去重,适合共同好友、抽奖
ZSet:有序集合,适合排行榜、积分系统
总结
选择合适的数据结构是使用 Redis 的关键。在实际项目中,根据业务需求选择合适的类型,可以提升性能和开发效率。