Redis 核心知识笔记 - 第二部分:持久化机制
Redis 核心知识笔记 - 第二部分:持久化机制
一、持久化概述
Redis 作为内存数据库,需要持久化机制保证数据安全。Redis 提供两种持久化方式:
| 对比项 | RDB | AOF |
|---|---|---|
| 持久化方式 | 定时快照 | 命令追加日志 |
| 数据安全性 | 可能丢失最后一次快照后的数据 | 根据策略,最多丢失 1 秒数据 |
| 文件大小 | 紧凑,体积小 | 较大,但可重写压缩 |
| 恢复速度 | 快 | 慢(需重放命令) |
| 性能影响 | fork 子进程时有短暂阻塞 | 写入时有 IO 开销 |
二、RDB 持久化
2.1 RDB 原理
RDB(Redis Database)通过创建数据快照实现持久化。
2.2 触发方式
手动触发
# 同步保存(阻塞主进程,生产环境禁用)
SAVE
# 后台保存(fork 子进程,推荐)
BGSAVE
# 查看最后一次 RDB 保存时间
LASTSAVE
自动触发
# redis.conf 配置
# 格式:save <seconds> <changes>
# 表示:在 seconds 秒内如果有 changes 次写操作,则触发 BGSAVE
save 900 1 # 900秒内至少1次修改
save 300 10 # 300秒内至少10次修改
save 60 10000 # 60秒内至少10000次修改
# 禁用 RDB
save ""
# RDB 文件名
dbfilename dump.rdb
# RDB 文件存储目录
dir /var/lib/redis
# bgsave 发生错误时停止写入
stop-writes-on-bgsave-error yes
# RDB 文件压缩(LZF 算法)
rdbcompression yes
# RDB 文件校验
rdbchecksum yes
2.3 RDB 优缺点
优点:
- ✅ 紧凑的二进制文件,适合备份和灾难恢复
- ✅ 恢复大数据集时速度比 AOF 快
- ✅ fork 子进程,不影响主进程性能
- ✅ 适合冷备份,可定期复制到远程存储
缺点:
- ❌ 可能丢失最后一次快照后的数据
- ❌ fork 大量数据时可能导致短暂停顿
- ❌ 频繁 fork 可能对 CPU 造成压力
2.4 RDB 文件结构
+-------+-------------+------------+-------+-----+-------+-----+-----+
| REDIS | RDB-VERSION | SELECT-DB | KEY | ... | KEY | EOF | CRC |
| 5B | 4B | 1B | PAIRS | ... | PAIRS | 1B | 8B |
+-------+-------------+------------+-------+-----+-------+-----+-----+
三、AOF 持久化
3.1 AOF 原理
AOF(Append Only File)以日志形式记录每个写操作命令。
3.2 AOF 配置
# 开启 AOF
appendonly yes
# AOF 文件名
appendfilename "appendonly.aof"
# 同步策略
# always: 每次写操作都同步,最安全但最慢
# everysec: 每秒同步一次,折中方案(推荐)
# no: 由操作系统决定,最快但可能丢失较多数据
appendfsync everysec
# AOF 重写期间是否暂停同步
no-appendfsync-on-rewrite no
# 自动触发重写的条件
# 当前 AOF 文件大小超过上次重写后的 100%
auto-aof-rewrite-percentage 100
# 最小触发重写的 AOF 文件大小
auto-aof-rewrite-min-size 64mb
3.3 AOF 同步策略对比
| 策略 | 说明 | 性能 | 数据安全性 |
|---|---|---|---|
| always | 每次写操作都 fsync | 最慢 | 最高,最多丢失一条命令 |
| everysec | 每秒 fsync 一次 | 适中 | 较高,最多丢失 1 秒数据 |
| no | 由 OS 决定 fsync 时机 | 最快 | 较低,可能丢失较多数据 |
3.4 AOF 重写机制
AOF 文件会不断增大,需要通过重写来压缩。
重写触发方式:
# 手动触发
BGREWRITEAOF
# 自动触发(配置文件)
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
重写原理示例:
# 原始 AOF 记录
SET counter 1
INCR counter
INCR counter
INCR counter
DEL counter
SET counter 100
# 重写后只保留最终状态
SET counter 100
3.5 AOF 优缺点
优点:
- ✅ 数据安全性更高,最多丢失 1 秒数据
- ✅ AOF 文件是追加写入,无损坏风险
- ✅ 可读性好,便于分析和修复
- ✅ 支持重写压缩
缺点:
- ❌ 文件体积通常比 RDB 大
- ❌ 恢复速度比 RDB 慢
- ❌ 每秒同步模式下,性能略低于 RDB
3.6 AOF 文件格式(RESP 协议)
*3 # 数组,包含3个元素
$3 # 第1个元素长度为3
SET # 第1个元素内容
$3 # 第2个元素长度为3
key # 第2个元素内容
$5 # 第3个元素长度为5
value # 第3个元素内容
四、混合持久化(Redis 4.0+)
4.1 混合持久化原理
结合 RDB 和 AOF 的优点:重写时先写入 RDB 格式,后续增量写入 AOF 格式。
4.2 配置方式
# 开启 AOF
appendonly yes
# 开启混合持久化(Redis 4.0+)
aof-use-rdb-preamble yes
4.3 混合持久化优势
| 对比项 | 纯 RDB | 纯 AOF | 混合持久化 |
|---|---|---|---|
| 启动速度 | 快 | 慢 | 快 |
| 数据安全 | 较低 | 高 | 高 |
| 文件大小 | 小 | 大 | 适中 |
五、数据恢复
5.1 恢复优先级
Important
如果同时开启 RDB 和 AOF,Redis 优先使用 AOF 恢复数据,因为 AOF 数据更完整。
5.2 RDB 恢复
# 1. 停止 Redis
redis-cli shutdown
# 2. 备份当前 RDB 文件
cp /var/lib/redis/dump.rdb /var/lib/redis/dump.rdb.bak
# 3. 复制备份的 RDB 文件到 Redis 数据目录
cp /backup/dump.rdb /var/lib/redis/dump.rdb
# 4. 确保 AOF 关闭或移除 AOF 文件
# 如果开启了 AOF,需要先关闭或移除 AOF 文件
# 5. 启动 Redis
redis-server /etc/redis/redis.conf
5.3 AOF 恢复
# 1. 停止 Redis
redis-cli shutdown
# 2. 如果 AOF 文件损坏,先修复
redis-check-aof --fix appendonly.aof
# 3. 复制备份的 AOF 文件
cp /backup/appendonly.aof /var/lib/redis/appendonly.aof
# 4. 启动 Redis
redis-server /etc/redis/redis.conf
5.4 AOF 文件修复
# 检查 AOF 文件
redis-check-aof appendonly.aof
# 修复损坏的 AOF 文件
redis-check-aof --fix appendonly.aof
# 修复会截断损坏部分,可能丢失部分数据
六、持久化方案选择
6.1 选择建议决策图
6.2 场景推荐
| 场景 | 推荐方案 | 配置建议 |
|---|---|---|
| 纯缓存 | 关闭持久化或仅 RDB | save "" |
| 一般业务 | 混合持久化 | appendonly yes + aof-use-rdb-preamble yes |
| 金融场景 | AOF + always | appendfsync always |
| 大数据量 | RDB + 主从复制 | 定时 BGSAVE + 从库备份 |
6.3 生产环境配置示例
# ============= 推荐配置(混合持久化)=============
# RDB 配置
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis
# AOF 配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 混合持久化
aof-use-rdb-preamble yes
七、Java 代码示例
7.1 手动触发持久化
@Service
public class RedisPersistenceService {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 触发 BGSAVE
*/
public void triggerBgsave() {
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.bgSave();
return null;
});
}
/**
* 触发 BGREWRITEAOF
*/
public void triggerBgrewriteaof() {
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.bgReWriteAof();
return null;
});
}
/**
* 获取最后保存时间
*/
public Long getLastSaveTime() {
return redisTemplate.execute((RedisCallback<Long>) connection ->
connection.lastSave()
);
}
/**
* 获取持久化信息
*/
public Properties getPersistenceInfo() {
return redisTemplate.execute((RedisCallback<Properties>) connection ->
connection.info("persistence")
);
}
}
7.2 备份脚本
#!/bin/bash
# Redis 备份脚本
REDIS_CLI=/usr/bin/redis-cli
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD="your_password"
BACKUP_DIR=/backup/redis
DATE=$(date +%Y%m%d_%H%M%S)
RDB_FILE=/var/lib/redis/dump.rdb
# 创建备份目录
mkdir -p $BACKUP_DIR
# 触发 BGSAVE
$REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD BGSAVE
# 等待 BGSAVE 完成
while [ $($REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD LASTSAVE) == $($REDIS_CLI -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD LASTSAVE) ]; do
sleep 1
done
# 复制 RDB 文件
cp $RDB_FILE $BACKUP_DIR/dump_$DATE.rdb
# 压缩备份文件
gzip $BACKUP_DIR/dump_$DATE.rdb
# 删除 7 天前的备份
find $BACKUP_DIR -name "dump_*.rdb.gz" -mtime +7 -delete
echo "Backup completed: $BACKUP_DIR/dump_$DATE.rdb.gz"
八、常见问题与调优
8.1 fork 阻塞问题
问题:大数据量 fork 时可能造成主进程阻塞
解决方案:
# 1. 使用更快的存储设备(SSD)
# 2. 减少 RDB 频率
save 900 1
# 3. 关闭 THP(Transparent Huge Pages)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
# 4. 设置 vm.overcommit_memory = 1
sysctl vm.overcommit_memory=1
8.2 AOF 重写阻塞
问题:AOF 重写期间性能下降
解决方案:
# 在低峰期手动触发重写
BGREWRITEAOF
# 调整自动重写阈值
auto-aof-rewrite-percentage 200
auto-aof-rewrite-min-size 128mb
8.3 持久化监控指标
# 查看持久化状态
INFO persistence
# 关键指标
rdb_last_bgsave_status: ok # 最后一次 BGSAVE 状态
rdb_last_bgsave_time_sec: 3 # 最后一次 BGSAVE 耗时
aof_last_rewrite_time_sec: 2 # 最后一次 AOF 重写耗时
aof_current_size: 12345678 # 当前 AOF 文件大小
aof_base_size: 1234567 # 上次重写后的 AOF 大小