跳到主要内容

Redis 持久化

持久化是将内存中的数据保存到磁盘的过程。Redis 提供了 RDB 和 AOF 两种持久化方式,本章将详细介绍这两种方式的原理和配置。

持久化概述

Redis 作为内存数据库,如果不进行持久化,重启后数据会全部丢失。持久化机制确保数据在服务器重启后能够恢复。

                    Redis 持久化方式

┌─────────────────────────────────────────────────┐
│ Redis 内存数据 │
└─────────────────────────────────────────────────┘

┌─────────────┴─────────────┐
│ │
┌─────────────────┐ ┌─────────────────┐
│ RDB 快照 │ │ AOF 日志 │
│ 二进制文件 │ │ 文本文件 │
│ 定时保存 │ │ 追加写入 │
│ 恢复速度快 │ │ 数据更安全 │
└─────────────────┘ └─────────────────┘

RDB 持久化

RDB(Redis Database)是将某一时刻的内存数据保存到磁盘的二进制文件(默认为 dump.rdb)。

RDB 工作原理

RDB 使用快照的方式保存数据,通过 BGSAVE 命令触发:

  1. 客户端触发 BGSAVE 命令
  2. Redis 主进程 fork 子进程 - 使用写时复制(Copy-on-Write)技术
  3. 子进程将内存数据写入临时 RDB 文件
  4. 子进程完成,替换旧的 RDB 文件
  5. 主进程继续处理客户端请求

fork 操作使用写时复制技术,子进程和父进程共享内存页,只有当数据被修改时才会复制,因此 fork 操作通常很快。

RDB 配置

redis.conf 中配置 RDB:

# 触发快照的条件
# save <秒数> <变化次数>
save 900 1 # 900 秒内有 1 次变化
save 300 10 # 300 秒内有 10 次变化
save 60 10000 # 60 秒内有 10000 次变化

# 禁用 RDB(注释掉所有 save 或设置空字符串)
# save ""

# RDB 文件名
dbfilename dump.rdb

# 数据目录
dir /var/lib/redis

# 压缩(默认开启)
rdbcompression yes

# 校验和(默认开启)
rdbchecksum yes

# RDB 文件存储失败时是否停止写入
stop-writes-on-bgsave-error yes

RDB 命令

# 手动触发快照(后台执行)
BGSAVE

# 手动触发快照(前台执行,会阻塞)
SAVE

# 获取上次保存时间
LASTSAVE

# 查看持久化状态
INFO persistence

RDB 优缺点

优点

  • 文件紧凑,适合备份
  • 恢复速度快
  • 对性能影响小(子进程执行)
  • 适合灾难恢复

缺点

  • 数据安全性较低(可能丢失几分钟数据)
  • fork 大数据集时可能阻塞

AOF 持久化

AOF(Append Only File)记录所有写操作命令,恢复时重新执行这些命令。

AOF 工作原理

AOF 以日志的形式记录每个写操作,文件以 Redis 协议格式追加保存。每次执行改变数据集的命令时,Redis 会将该命令追加到 AOF 文件末尾。

Redis 7.0+ 的 Multi-Part AOF

Redis 7.0 重构了 AOF 架构,将单个 AOF 文件拆分为多个文件:

文件类型说明
Base AOF重写时生成的基础快照(RDB 或 AOF 格式)
Incremental AOF增量日志文件,记录上次 Base 后的变更
Manifest File清单文件,追踪所有 AOF 文件的状态

Multi-Part AOF 的优势

维度Redis < 7.0Redis >= 7.0
重写期间内存压力高(内存缓冲区)低(直接写入增量文件)
磁盘写入双写(旧 AOF + 新 AOF)单写(增量文件)
重写失败风险可能丢失缓冲区数据数据安全(增量文件持久化)
文件管理单文件多文件,自动清理旧文件

目录结构示例

appendonlydir/
├── appendonly.aof.1.base.rdb # Base AOF(RDB 格式)
├── appendonly.aof.1.incr.aof # 第一个增量文件
├── appendonly.aof.2.incr.aof # 第二个增量文件
└── appendonly.aof.manifest # 清单文件

AOF 配置

# redis.conf 配置

# 启用 AOF
appendonly yes

# AOF 文件名
appendfilename "appendonly.aof"

# AOF 目录(Redis 7.0+,存放所有 AOF 相关文件)
appenddirname "appendonlydir"

# 同步策略
# always : 每次写入都同步,最安全但最慢
# everysec : 每秒同步一次(推荐,平衡性能和安全)
# no : 由操作系统决定,最快但不安全
appendfsync everysec

# 重写期间是否禁用 fsync
no-appendfsync-on-rewrite no

# AOF 重写触发条件
auto-aof-rewrite-percentage 100 # 文件比上次重写后增长 100%
auto-aof-rewrite-min-size 64mb # 文件最小 64MB

# 加载损坏的 AOF 文件是否继续
aof-load-truncated yes

# 使用 RDB-AOF 混合格式(Redis 4.0+,强烈推荐)
aof-use-rdb-preamble yes

AOF 重写

AOF 文件会随着写操作不断增长,重写可以压缩文件大小。

重写原理

Redis >= 7.0 的重写流程

┌─────────────────────────────────────────────────────────────┐
│ Redis 7.0+ AOF 重写流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 主进程 fork 子进程 │
│ │
│ 2. 主进程打开新的增量文件继续记录新写入 │
│ └── 不再使用内存缓冲区,直接写入磁盘 │
│ │
│ 3. 子进程生成新的 Base AOF 文件 │
│ └── 包含当前数据集的最小命令集合 │
│ │
│ 4. 子进程完成后,主进程创建临时 manifest 文件 │
│ └── 记录新的 Base 和增量文件 │
│ │
│ 5. 原子替换 manifest 文件 │
│ └── 使用 rename(2) 系统调用 │
│ │
│ 6. 清理旧的 Base 文件和无用的增量文件 │
│ │
└─────────────────────────────────────────────────────────────┘

Redis < 7.0 的重写流程

┌─────────────────────────────────────────────────────────────┐
│ Redis < 7.0 AOF 重写流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 主进程 fork 子进程 │
│ │
│ 2. 主进程将新写入缓存到内存缓冲区 │
│ └── 同时继续写入旧 AOF 文件(保证安全) │
│ │
│ 3. 子进程生成新的 AOF 文件 │
│ │
│ 4. 子进程完成后,主进程将缓冲区追加到新文件 │
│ └── 此时可能阻塞客户端 │
│ │
│ 5. 原子重命名新文件替换旧文件 │
│ │
└─────────────────────────────────────────────────────────────┘

对比说明

阶段Redis < 7.0Redis >= 7.0
重写期间新写入内存缓冲区 + 旧 AOF新增量文件
内存压力
阻塞风险追加缓冲区时可能阻塞无阻塞
数据安全依赖内存缓冲区直接持久化到磁盘

手动触发重写

# 手动触发重写
BGREWRITEAOF

# 查看重写状态
INFO persistence
# 关注 aof_rewrite_in_progress 和 aof_last_bgrewrite_status

重写限制机制

Redis 7.0 引入了重写限制机制,避免重复失败导致频繁重试:

  • 首次失败后等待 1 分钟重试
  • 再次失败后等待 2 分钟
  • 依次类推,最长等待 1 小时

AOF 文件修复

截断文件(文件末尾不完整):

# Redis 会自动处理截断的 AOF 文件
# 默认配置 aof-load-truncated yes 会自动丢弃末尾不完整的命令

# 手动修复
redis-check-aof --fix appendonly.aof

损坏文件(中间有无效字节):

# 1. 先备份文件
cp appendonly.aof appendonly.aof.backup

# 2. 不带 --fix 检查,定位问题
redis-check-aof appendonly.aof

# 3. 修复(会丢弃损坏位置之后的数据)
redis-check-aof --fix appendonly.aof

AOF 优缺点

优点

  • 数据更安全(最多丢失 1 秒数据)
  • AOF 文件可读,便于分析
  • 文件损坏可修复
  • Redis 7.0+ 重写期间无阻塞

缺点

  • 文件体积较大
  • 恢复速度较慢
  • Redis < 7.0 重写期间有内存压力

RDB 与 AOF 对比

特性RDBAOF
数据安全性较低(分钟级丢失)较高(秒级丢失)
文件大小
恢复速度
系统资源消耗低(fork 时)较高(持续写入)
文件可读性二进制文本
适用场景备份、主从复制数据安全要求高

混合持久化

Redis 4.0 引入混合持久化,结合 RDB 和 AOF 的优点:

# 启用混合持久化
aof-use-rdb-preamble yes

工作原理

  • AOF 重写时,先写入 RDB 格式的快照
  • 然后追加增量的 AOF 命令
  • 恢复时先加载 RDB 部分,再执行 AOF 命令

这样既有 RDB 的快速恢复,又有 AOF 的数据安全性。

持久化配置建议

场景一:纯缓存(可接受数据丢失)

# 关闭持久化
save ""
appendonly no

场景二:数据安全要求高

# 开启 AOF
appendonly yes
appendfsync everysec

# 也保留 RDB 用于备份
save 900 1
save 300 10

场景三:平衡方案(推荐)

# 开启 AOF 和混合持久化
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes

# RDB 用于快速重启和备份
save 900 1
save 300 10
save 60 10000

数据恢复

RDB 恢复

# 1. 停止 Redis
redis-cli shutdown

# 2. 将 dump.rdb 复制到数据目录
cp /backup/dump.rdb /var/lib/redis/

# 3. 启动 Redis
redis-server /etc/redis/redis.conf

AOF 恢复

# 1. 停止 Redis
redis-cli shutdown

# 2. 复制 AOF 文件
cp /backup/appendonly.aof /var/lib/redis/

# 3. 修复损坏的 AOF(如需要)
redis-check-aof --fix appendonly.aof

# 4. 启动 Redis
redis-server /etc/redis/redis.conf

从 RDB 切换到 AOF

# 在线切换(Redis 2.2+)
redis-cli CONFIG SET appendonly yes
redis-cli CONFIG SET save ""

# 持久化配置
redis-cli CONFIG REWRITE

备份策略

#!/bin/bash
# Redis 备份脚本

BACKUP_DIR="/backup/redis"
DATE=$(date +%Y%m%d_%H%M%S)

# 创建备份目录
mkdir -p $BACKUP_DIR

# 触发 RDB 快照
redis-cli BGSAVE

# 等待快照完成
sleep 5

# 复制 RDB 文件
cp /var/lib/redis/dump.rdb $BACKUP_DIR/dump_$DATE.rdb

# 压缩
gzip $BACKUP_DIR/dump_$DATE.rdb

# 删除 7 天前的备份
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete

echo "Backup completed: dump_$DATE.rdb.gz"

将脚本添加到 crontab 定时执行:

# 每天凌晨 2 点备份
0 2 * * * /path/to/backup.sh

小结

本章我们学习了:

  1. RDB 持久化:快照方式,恢复快但可能丢数据
  2. AOF 持久化:日志方式,数据安全但文件大
  3. 混合持久化:结合两者优点
  4. 配置建议:根据场景选择合适策略
  5. 数据恢复:RDB 和 AOF 的恢复方法

练习

  1. 配置 RDB 持久化并手动触发快照
  2. 配置 AOF 持久化并观察文件变化
  3. 模拟 Redis 重启并恢复数据
  4. 编写自动备份脚本

参考资料