1. 持久化机制的作用
Redis 是基于内存的数据结构数据库,虽然读写性能非常高,但所有数据默认保存在内存中。一旦服务器宕机、进程意外崩溃或容器重启,内存中的数据将全部丢失。这对于生产环境的可用性与可靠性是极其危险的。因此,Redis 提供了持久化机制,将内存数据定期或实时写入磁盘,以便在服务重启时能够从磁盘数据中恢复,防止宕机导致的数据全部丢失。
2. 三种持久化机制
Redis 提供了三种持久化方式:
- RDB(Redis DataBase)快照持久化
- AOF(Append Only File)日志持久化
- 混合持久化(RDB + AOF,Redis 4.0+ 引入)
在生产环境中使用时,这些策略都可能会失败。
2.1 RDB 快照持久化
1 基本原理
RDB 是一种 周期性快照机制,Redis 会在满足特定条件时,将当前内存中的所有数据以快照方式保存为一个二进制文件(默认为 dump.rdb
)。
RDB 采用了 COW(Copy-On-Write)机制,由 Redis 主进程通过 fork()
创建子进程,子进程将内存快照写入磁盘,而主进程则继续处理客户端请求,保证高并发。
子进程刚创建时共享父进程内存,但当父进程修改某块内存数据时,会将该页面复制一份,以避免子进程快照不一致,这一机制称为 “页面分离”。
2 触发方式
- 自动触发:通过配置
redis.conf
文件中的save
参数,如:
//默认如下配置:
save 900 1:每隔900s(15min),如果有超过1个key发生了变化,就写一份新的RDB文件
save 300 10:每隔300s(5min),如果有超过10个key发生了变化,就写一份新的RDB文件
save 60 10000:每隔60s(1min),如果有超过10000个key发生了变化,就写一份新的RDB文件
//(配置多种策略可以同时生效,无论满足哪一种条件都会写一份新的RDB文件)
- 手动触发:
save
:阻塞主进程,生成 RDB 文件。bgsave
:异步触发,fork 子进程处理持久化。
3 优缺点
优点:对主进程性能影响小,快照文件便于备份与远程传输。恢复速度快,适合冷启动。数据文件紧凑,占用空间小。
缺点:数据可能丢失几分钟。 fork 子进程时内存翻倍,数据量大时存在性能抖动风险。使用特定二进制格式存储,版本兼容性差。
4 RDB可能的故障场景
场景1:数据丢失。默认配置下,如果Redis在快照之间崩溃,可能丢失最多15分钟数据。
场景2:Fork失败。当内存不足时,fork操作可能失败。
# Redis log
[1111] 4 Aug # Can't save in background: fork: Cannot allocate memory
场景3:磁盘空间耗尽。RDB文件先写入临时位置,再原子性的重命名。如果写入期间磁盘空间不足,快照会失败,Redis仍然继续运行。
# Redis log
[1111] 4 Aug # Write error saving DB on disk: No space left on device
场景4:断电后RDB文件损坏。断电时RDB文件只写入部分内容。重启时Redis拒绝启动损坏的RDB文件。
# Redis startup log
[1111] 4 Aug # Short read or OOM loading DB. Unrecoverable error, aborting now.
2.2 AOF 日志持久化
1 基本原理
AOF 通过将所有修改命令(如 SET
、DEL
)按顺序追加到日志文件(默认为 appendonly.aof
)中来实现持久化。Redis 在收到命令后,先记录日志,再执行命令。写入流程如下:
- 指令被追加至 OS 缓冲区;
- 根据
appendfsync
策略定期 fsync 到磁盘。
2 配置方式
通过 appendfsync
参数配置:
appendfsync everysec # 每秒刷一次
appendfsync always # 每写入一条命令都 fsync,最安全但最慢
appendfsync no # 不主动 fsync,完全交由 OS 决定
3 重写(Rewrite)
随着操作积累,AOF 文件会越来越大,影响重启速度。因此 Redis 提供了AOF Rewrite:
- fork 子进程;
- 将当前内存快照转化为最小指令集;
- 合并重写后的小文件;
- 替换原有 AOF 文件。
// 自动触发规
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
4 优缺点
优点:更高的数据安全性,最多丢失 1 秒数据。日志可读,可手动恢复误删数据。重写机制可控制文件大小。
缺点:恢复速度比 RDB 慢。写入性能略低于 RDB。文件体积通常比 RDB 大。
5 AOF可能的失败场景
场景1:系统崩溃后AOF损坏
# Redis startup log
[1111] 4 Aug # Bad file format reading the append only file:
make a backup of your AOF file, then use ./redis-check-aof --fix <filename>
场景2:AOF重写失败。随着AOF文件增长,Redis会定期重写以保持精简。但这个重写过程可能出错。
# Redis log during rewrite failure
[1111] 4 Aug # Error rewriting AOF: No space left on device
[1111] 4 Aug # AOF rewrite failed:
Previous AOF was kept, but disk is full, cannot proceed with rewrite.
这个错误的大意是重写AOF错误,因为设备剩余存储空间不足。虽然重写失败,但Redis继续向旧AOF文件追加。磁盘完全填满后,Redis开始拒绝写入。
场景3:重写期间内存爆炸。AOF重写期间,Redis在内存中缓冲所有新写入。如果重写耗时过长(慢磁盘常见),可能在"安全"的重写操作期间因OOM而崩溃。
场景4:"appendfsync always"导致脑裂。假设对Redis中的重要数据使用appendfsync always
策略。如果这个Redis实例因为网络问题等性能变差,会被负载均衡器将其标记为不健康,把流量转向备份实例。网络恢复后,会得到两个包含不同数据的Redis实例。
2.3 混合持久化(RDB + AOF)
Redis 4.0 引入混合持久化模式:在重写 AOF 文件时,先写入 RDB 快照,再追加从快照开始到当前的 AOF 增量日志,融合了二者优点。
配置参数:
aof-use-rdb-preamble yes
注意:混合持久化的 AOF 文件前段为 RDB 格式,老版本 Redis 不兼容。
优点:
- 恢复效率高,RDB 快照 + 小量 AOF 日志。
- 兼具 AOF 的数据完整性与 RDB 的恢复速度。
缺点:
- 文件结构复杂,可读性差。
- 4.0 以下版本不支持。
3. 持久化监控信息
Redis的INFO
命令能显示持久化状态,但信息并不完整。
# INFO显示内容
rdb_last_save_time:1678889001
rdb_last_bgsave_status:ok
aof_enabled:1
aof_last_rewrite_time_sec:45# 未显示内容
# - Fork失败率
# - 磁盘空间趋势
# - 损坏检测
# - 性能影响
以下是一个设置额外的监控数据的示例。
#!/bin/bash
# 检查RDB保存失败
redis-cli LASTSAVE | awk '{last_save = $1;now = systime();diff = now - last_save;if (diff > 3600) { # 1小时未保存则告警print "警告:RDB已" diff "秒未保存";}
}'# 检查AOF健康状态
redis-cli INFO persistence | grep aof_last_write_status | grep -v ok && echo "AOF写入失败"# 监控持久化磁盘空间
df -h /var/lib/redis | awk 'NR==2 {if (int($5) > 80) {print "警告:Redis磁盘使用" $5 "已满";}
}'
4. 恢复策略
1. AOF损坏恢复参考示例
# 步骤1:备份损坏文件
cp appendonly.aof appendonly.aof.corrupted# 步骤2:尝试自动修复
redis-check-aof --fix appendonly.aof# 步骤3:检查丢失内容
redis-check-aof appendonly.aof.corrupted > corruption_report.txt# 步骤4:自动修复失败时人工干预
# 编辑AOF文件,移除末尾不完整命令
tail -20 appendonly.aof.corrupted # 查找不完整命令
head -n -5 appendonly.aof.corrupted > appendonly.aof # 移除最后5行
2 RDB损坏恢复参考示例
# 步骤1:检查RDB是否可修复
redis-check-rdb dump.rdb# 步骤2:不可修复则从备份恢复
aws s3 cp s3://backup-bucket/redis/dump.rdb.gz .
gunzip dump.rdb.gz# 步骤3:无备份则重建并重载应用数据
5. 完整配置参考
1 Redis配置
# redis.conf - 生产环境配置# RDB配置
# 更频繁保存,多条件触发
save 900 1 # 15分钟内任何变更
save 300 10 # 5分钟内10+变更
save 60 100 # 1分钟内100+变更# RDB失败时停止接受写入
stop-writes-on-bgsave-error yes# 压缩RDB文件
rdbcompression yes# AOF配置
appendonly yes
appendfilename "appendonly.aof"# 每秒同步(良好的持久性/性能平衡)
appendfsync everysec# 启用AOF重写控制文件大小
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb# AOF损坏但可读时不丢失数据
aof-load-truncated yes# 使用RDB-AOF混合加速重启
aof-use-rdb-preamble yes# 内存管理
maxmemory 6gb # 为fork期间的COW预留空间
maxmemory-policy allkeys-lru# 禁用内存过量使用
vm-enabled no
2 自动备份配置
#!/bin/bash
# redis-backup.sh - 每小时执行DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backup/redis"
S3_BUCKET="company-redis-backups"# 创建备份目录
mkdir -p $BACKUP_DIR/$DATE# 触发RDB保存
redis-cli BGSAVE
sleep 30 # 等待保存完成# 复制文件
cp /var/lib/redis/dump.rdb $BACKUP_DIR/$DATE/
cp /var/lib/redis/appendonly.aof $BACKUP_DIR/$DATE/# 压缩上传
tar -czf $BACKUP_DIR/$DATE.tar.gz $BACKUP_DIR/$DATE/
aws s3 cp $BACKUP_DIR/$DATE.tar.gz s3://$S3_BUCKET/# 清理旧备份(本地保留48小时,S3保留30天)
find $BACKUP_DIR -name "*.tar.gz" -mtime +2 -delete
aws s3 ls s3://$S3_BUCKET/ | awk '$1 < "'$(date -d '30 days ago' '+%Y-%m-%d')'" {print $4}' | xargs -I {} aws s3 rm s3://$S3_BUCKET/{}
6. 常用持久化相关命令
命令 | 说明 |
---|---|
save | 阻塞生成 RDB 快照 |
bgsave | 后台生成 RDB 快照 |
bgrewriteaof | 重写 AOF 文件 |
info Persistence | 查看持久化信息 |
redis-check-aof | 检查 / 修复 AOF 文件 |
redis-check-rdb | 检查 RDB 文件 |
shutdown | 安全关闭,自动保存 RDB |
kill -9 | 强制关闭,可能导致数据丢失 |