Redis

Redis 核心知识笔记 - 第二部分:持久化机制

Redis 核心知识笔记 - 第二部分:持久化机制

一、持久化概述

Redis 作为内存数据库,需要持久化机制保证数据安全。Redis 提供两种持久化方式:

对比项RDBAOF
持久化方式定时快照命令追加日志
数据安全性可能丢失最后一次快照后的数据根据策略,最多丢失 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 场景推荐

场景推荐方案配置建议
纯缓存关闭持久化或仅 RDBsave ""
一般业务混合持久化appendonly yes + aof-use-rdb-preamble yes
金融场景AOF + alwaysappendfsync 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 大小