一、结论先给
| 方案 | 数据安全 | 性能影响 | 恢复速度 | 适用 |
|---|---|---|---|---|
| 仅 RDB | 丢最后一次快照之后的数据(可能几分钟) | 小 | 快 | 纯缓存、可重建 |
| 仅 AOF | 最多丢 1 秒(everysec) | 中 | 慢(要重放命令) | 要尽量不丢 |
| RDB + AOF 混合(推荐) | 最多丢 1 秒 | 中 | 快 | 绝大多数场景 |
结论:生产环境开 appendonly yes + aof-use-rdb-preamble yes(混合持久化,Redis 4.0+ 默认开启),这是兼顾安全与恢复速度的标准配置。
但记住:Redis 再怎么持久化也不如关系型数据库可靠,缓存丢了能重建,唯一数据源必须放数据库。
👉 排查持久化/复制故障时,把 Redis 日志粘进 日志分析工具 能快速定位错误类型和高发时段。
二、RDB:时间点快照
2.1 机制
RDB 是把某一刻的全量数据写成二进制快照(dump.rdb)。
SAVE -- 同步保存 ⚠️ 阻塞主线程,生产禁用
BGSAVE -- ⭐ 后台保存,fork 子进程写
LASTSAVE -- 上次成功保存的时间戳
自动触发配置:
# redis.conf:900 秒内至少 1 次改动则触发,300 秒内 10 次,60 秒内 10000 次
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir /var/lib/redis
stop-writes-on-bgsave-error yes # 快照失败时停止写入,提醒你磁盘有问题
rdbcompression yes # 压缩(LZF),CPU 换空间
rdbchecksum yes # 校验和
2.2 RDB 的两个要点
1. fork 的代价:BGSAVE 会 fork 子进程,采用写时复制(COW)。父进程内存越大,fork 时的页表拷贝越慢(几个 G 的实例可能阻塞几十到几百毫秒)。数据量很大时要监控 latest_fork_usec:
INFO stats | grep latest_fork_usec # 上次 fork 耗时(微秒),超过 1 秒要警惕
2. 丢失窗口:两次快照之间的数据会丢。按 save 60 10000 算,最坏丢 1 分钟的数据。
三、AOF:命令日志
3.1 机制与刷盘策略
AOF 把每个写命令追加到日志文件,重启时重放。
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec # ⭐ 三选一
appendfsync |
行为 | 最多丢 | 性能 |
|---|---|---|---|
always |
每条命令都刷盘 | 几乎不丢 | 最差(不推荐) |
everysec |
每秒刷一次 | 1 秒(默认,推荐) | 好 |
no |
交给操作系统决定 | 可能几十秒 | 最好(不推荐) |
3.2 AOF 重写
AOF 会越来越大(对同一个 key 改 100 次就记 100 条),所以要重写:根据当前内存中的数据生成一份最小的命令集。
BGREWRITEAOF -- 手动触发后台重写
自动触发:
auto-aof-rewrite-percentage 100 # 比上次重写后增长了 100%
auto-aof-rewrite-min-size 64mb # 且文件至少 64MB
no-appendfsync-on-rewrite no # 重写时是否暂停 fsync;yes 可减少阻塞但可能丢更多
⚠️ 重写的坑:auto-aof-rewrite-min-size 太小会导致频繁重写;重写期间需要额外内存(fork + 缓冲区),要确保机器有足够空闲内存,否则会 OOM。
3.3 混合持久化(推荐)
aof-use-rdb-preamble yes # Redis 4.0+ 默认 yes
开启后 AOF 重写时,前半段用 RDB 二进制格式、后半段追加增量 AOF 命令。兼顾了 RDB 的加载速度和 AOF 的低丢失窗口。
文件结构长这样:
[RDB 格式的全量数据][AOF 格式的增量命令]
验证:
head -c 100 appendonly.aof | strings | head -5 # 能看到 "REDIS" 字样说明含 RDB 段
3.4 AOF 文件损坏修复
redis-check-aof --fix appendonly.aof
redis-check-rdb dump.rdb
修复会截断损坏部分(可能丢数据),执行前先备份原文件。
四、数据恢复与备份
# 备份就是拷贝文件(AOF 优先于 RDB 加载)
cp /var/lib/redis/appendonly.aof /data/backup/appendonly_$(date +%F).aof
cp /var/lib/redis/dump.rdb /data/backup/dump_$(date +%F).rdb
# 恢复:停 Redis → 放文件 → 改权限 → 启动
systemctl stop redis
cp appendonly_xxx.aof /var/lib/redis/appendonly.aof
chown redis:redis /var/lib/redis/appendonly.aof
systemctl start redis
加载优先级:AOF 开启时优先加载 AOF 文件(数据更新更完整),AOF 关闭才加载 RDB。
⚠️ RDB 和 AOF 同时存在时,不会合并两份数据,只加载 AOF。
五、主从复制
5.1 配置
从库 redis.conf(Redis 5.0+ 用 replicaof,旧版是 slaveof):
replicaof 10.0.0.10 6379 # 主库地址
masterauth '主库密码' # 主库有密码时必须配
replica-read-only yes # 从库只读(默认)
repl-diskless-sync yes # 无盘复制(网络好时更快,避免写磁盘)
repl-backlog-size 64mb # 复制积压缓冲区,决定断线后能否增量续传
也可以在运行时生效(重启失效):
REPLICAOF 10.0.0.10 6379
REPLICAOF NO ONE -- ⭐ 提升为主库(故障切换时用)
5.2 复制状态
INFO replication
-- 主库视角:
-- role:master
-- connected_slaves:2
-- slave0:ip=10.0.0.11,port=6379,state=online,offset=123456,lag=0
-- 从库视角:
-- role:slave
-- master_link_status:up ⭐ 必须 up
-- master_last_io_seconds_ago:1
-- slave_repl_offset:123456
关键字段:master_link_status:up、lag(延迟秒数),offset 差值是真实延迟。
5.3 全量同步 vs 增量同步
| 类型 | 触发 | 代价 |
|---|---|---|
| 全量 | 从库首次连、offset 不在积压缓冲区内 | 主库 BGSAVE + 传整个 RDB + 从库加载,大实例很重 |
| 增量 | 短暂断连且 offset 还在 repl-backlog 里 |
只传缺失的命令,很轻 |
避免全量同步的关键:把 repl-backlog-size 调大(默认 1MB 太小,建议 64-256MB),让短暂断网能走增量。
repl-backlog-size 128mb
repl-backlog-ttl 3600
client-output-buffer-limit slave 512mb 128mb 300 # 放宽从库输出缓冲,防同步被中断
全量同步反复发生的排查:
INFO stats | grep sync_full -- 全量同步次数
INFO stats | grep sync_partial_ok -- 增量成功次数
六、哨兵(Sentinel):自动故障转移
6.1 配置
# /etc/redis/sentinel.conf(每个哨兵节点都配)
port 26379
sentinel monitor mymaster 10.0.0.10 6379 2 # 2 = 几个哨兵认为挂了才判定客观下线
sentinel auth-pass mymaster '主库密码'
sentinel down-after-milliseconds mymaster 5000 # 5 秒无响应判主观下线
sentinel parallel-syncs mymaster 1 # 故障转移后同时同步的从库数
sentinel failover-timeout mymaster 60000
启动:
redis-sentinel /etc/redis/sentinel.conf
# 或 redis-server /etc/redis/sentinel.conf --sentinel
⚠️ 哨兵至少要 3 个节点(且部署在不同机器),quorum 设 2。两个节点无法形成有效多数派。
6.2 选举流程
1. 某个哨兵发现主库 5 秒无响应 → 主观下线(sdown)
2. 它询问其他哨兵,达到 quorum 数量同意 → 客观下线(odown)
3. 哨兵之间选出一个 leader(Raft 式)
4. leader 挑一个从库:优先级高 → offset 大(数据新)→ runid 小
5. 对该从库执行 REPLICAOF NO ONE,再让其他从库指向新主库
6. 旧主库恢复后被降级为从库
从库优先级配置(想指定某台优先被提升):
replica-priority 100 # 数字越小优先级越高;设为 0 表示永不提升
6.3 客户端接入
客户端不能直连主库 IP,要连哨兵拿地址:
// Jedis
Set<String> sentinels = new HashSet<>(Arrays.asList(
"10.0.0.10:26379", "10.0.0.11:26379", "10.0.0.12:26379"));
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels, config);
// Spring Boot
spring:
redis:
sentinel:
master: mymaster
nodes: 10.0.0.10:26379,10.0.0.11:26379,10.0.0.12:26379
password: xxx
# 查询当前主库
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
redis-cli -p 26379 SENTINEL masters
redis-cli -p 26379 SENTINEL slaves mymaster
redis-cli -p 26379 SENTINEL ckquorum mymaster -- 检查哨兵数是否够
6.4 哨兵解决不了的问题
| 问题 | 说明 | 方案 |
|---|---|---|
| 主库写压力 | 只有一个主,写无法扩展 | Redis Cluster 分片 |
| 单机容量上限 | 数据超过单机内存 | Redis Cluster |
| 跨机房 | 网络分区可能双主 | 至少三机房部署,或接受脑裂风险 |
七、方案选型
| 规模/需求 | 方案 |
|---|---|
| 纯缓存、丢了能重建 | 单机 + RDB,甚至不开持久化 |
| 要尽量不丢、单实例够用 | 主从 + 哨兵 + 混合持久化 |
| 数据量大、写压力大 | Redis Cluster(3 主 3 从起) |
| 云上 | 云厂商托管版(自动主备、备份、扩缩容) |
八、常见误区
- 以为开了 AOF 就万无一失:
everysec仍可能丢 1 秒;Redis 持久化不等同于数据库的事务保证。 auto-aof-rewrite-min-size不调:默认 64MB 在大写入场景下频繁重写,IO 抖动。- 生产执行
SAVE:同步阻塞,几百万 key 能卡住几秒。 repl-backlog-size用默认 1MB:网络抖一下就触发全量同步,主库 IO 打满。- 哨兵只部署 2 个:无法形成多数派,等于没高可用。
- 客户端写死主库 IP:故障转移后应用连的还是旧主(已变从库),写入直接报错。
masterauth忘了配:从库同步一直失败,master_link_status:down。
九、几条纪律
- 生产开混合持久化(
appendonly yes+aof-use-rdb-preamble yes),appendfsync everysec。 repl-backlog-size调到 64-256MB,避免网络抖动触发全量同步。- 哨兵至少 3 节点、跨机器部署,
quorum= 节点数/2 + 1。 - 客户端通过哨兵/集群方式连接,绝不写死主库 IP。
- 定期备份 RDB/AOF 到异地,且验证过能恢复。
- 监控
master_link_status、latest_fork_usec、used_memory、sync_full四个指标。 - Redis 里不存唯一数据源,任何时刻都要能从数据库重建。
十、延伸阅读
👉 相关在线工具:Linux 命令速查 · 日志分析,免登录、浏览器内处理。