09 - 持久化
持久化
Redis 是基于内存的数据库,如果进程退出或服务器宕机,内存中的数据会丢失。持久化机制确保数据能从磁盘恢复。
9.1 持久化概述
| 方式 | 原理 | 数据安全性 | 恢复速度 | 文件大小 |
|---|---|---|---|---|
| RDB | 定时快照 | 中(可能丢失最后一次快照后的数据) | 快 | 小(压缩) |
| AOF | 记录写命令 | 高(最多丢失 1 秒数据) | 慢 | 大 |
| 混合持久化 | RDB + 增量 AOF | 高 | 快 | 中 |
9.2 RDB 快照
工作原理
RDB 在指定时间间隔内将内存中的数据快照写入磁盘(dump.rdb 文件)。恢复时将 RDB 文件加载到内存。
内存数据 ──BGSAVE──→ fork 子进程 ──写入──→ dump.rdb
(不影响主进程)
dump.rdb ──加载──→ 恢复内存数据
触发方式
# 1. 配置自动触发
# redis.conf
save 3600 1 # 3600 秒内至少 1 次修改
save 300 100 # 300 秒内至少 100 次修改
save 60 10000 # 60 秒内至少 10000 次修改
# 2. 手动触发 - 同步保存(阻塞主线程,生产慎用!)
SAVE
# 3. 手动触发 - 后台异步保存(推荐)
BGSAVE
# Background saving started
# 4. 关闭服务器时自动保存
SHUTDOWN # 自动触发 RDB 保存
# 5. 主从复制时,从节点首次同步触发
RDB 配置详解
# ---- 触发条件 ----
save 3600 1
save 300 100
save 60 10000
# 禁用 RDB(如果只用 AOF)
# save ""
# ---- 文件路径 ----
dbfilename dump.rdb
dir /var/lib/redis
# ---- 压缩 ----
rdbcompression yes # LZF 压缩(推荐开启)
rdbchecksum yes # CRC64 校验(推荐开启)
# ---- 错误处理 ----
stop-writes-on-bgsave-error yes # RDB 保存失败时停止写入
# ---- 性能 ----
rdb-save-incremental-fsync yes # 增量 fsync(减少写入峰值)
RDB 文件格式
┌──────────────────────────────────────────────┐
│ RDB 文件结构 │
├──────────────────────────────────────────────┤
│ REDIS + 版本号 (5 字节 + 4 字节) │
├──────────────────────────────────────────────┤
│ 辅助字段 (Redis 版本、创建时间、内存等) │
├──────────────────────────────────────────────┤
│ 数据库 0 │
│ ├── SELECTDB + 数据库编号 │
│ ├── RESIZEDB + 键值对数量 + 过期键数量 │
│ ├── 键值对数据(带过期时间则先写 EXPIRETIME │
│ └── ... │
├──────────────────────────────────────────────┤
│ 数据库 1 │
│ └── ... │
├──────────────────────────────────────────────┤
│ EOF 标记 │
├──────────────────────────────────────────────┤
│ CRC64 校验和 │
└──────────────────────────────────────────────┘
RDB 优缺点
| 优点 | 缺点 |
|---|---|
| 文件紧凑,适合备份和灾难恢复 | 可能丢失最后一次快照后的数据 |
| 恢复速度快(直接加载二进制) | 数据量大时 fork 子进程耗时 |
| 不影响主进程性能(BGSAVE) | RDB 文件版本兼容性问题 |
9.3 AOF 日志
工作原理
AOF 将每一个写命令追加到日志文件末尾(appendonly.aof)。恢复时重放所有命令。
客户端写入 ──→ 追加到 AOF 缓冲区 ──fsync──→ appendonly.aof
│
┌─────┴─────┐
│ │
always everysec
(每次写入) (每秒,推荐)
AOF 同步策略
| 策略 | 刷盘频率 | 数据安全 | 性能 |
|---|---|---|---|
always | 每次写命令 | 最高(不丢数据) | 最差 |
everysec | 每秒一次 | 高(最多丢 1 秒) | 好(推荐) |
no | 由操作系统决定 | 低(可能丢失大量数据) | 最好 |
# 查看当前 AOF 同步策略
redis-cli CONFIG GET appendfsync
# 1) "appendfsync"
# 2) "everysec"
# 运行时修改(不推荐频繁修改)
redis-cli CONFIG SET appendfsync everysec
AOF 配置详解
# ---- 开启 AOF ----
appendonly yes
appendfilename "appendonly.aof"
# ---- 同步策略 ----
appendfsync everysec
# ---- AOF 重写 ----
# 触发条件 1:AOF 文件比上次重写后增长了 100%
auto-aof-rewrite-percentage 100
# 触发条件 2:AOF 文件至少达到 64MB
auto-aof-rewrite-min-size 64mb
# 重写期间是否暂停 fsync(对数据安全有影响)
no-appendfsync-on-rewrite no
# ---- 文件格式 ----
# Redis 7.0+ 使用 Multi-Part AOF
aof-use-rdb-preamble yes # 混合持久化(推荐开启)
AOF 重写机制
随着时间推移,AOF 文件会不断增长。AOF 重写会创建一个新的精简 AOF 文件:
原始 AOF 文件:
SET key1 "a"
SET key1 "b" ← 覆盖前一条
DEL key2
SET key3 "c"
重写后 AOF 文件:
SET key1 "b" ← 只保留最新状态
SET key3 "c"
# 手动触发 AOF 重写
BGREWRITEAOF
# Background append only file rewriting started
# 查看 AOF 文件信息
redis-cli INFO persistence | grep aof
# aof_enabled:1
# aof_rewrite_in_progress:0
# aof_rewrite_scheduled:0
# aof_last_rewrite_status:ok
# aof_current_size:1048576
# aof_base_size:524288
AOF 文件损坏修复
# AOF 文件损坏时尝试修复
redis-check-aof --fix appendonly.aof
# 验证修复后的文件
redis-check-aof appendonly.aof
AOF 优缺点
| 优点 | 缺点 |
|---|---|
| 数据安全性高(最多丢 1 秒) | 文件比 RDB 大 |
| 可读性好(文本格式命令) | 恢复速度慢(重放命令) |
| 支持增量写入 | AOF 重写期间占用额外内存 |
9.4 混合持久化(Redis 4.0+)
混合持久化结合了 RDB 的快速加载和 AOF 的增量记录:
AOF 重写后的文件:
┌─────────────────────────────────────┐
│ RDB 格式的数据快照 │ ← 快速加载
│ (二进制,与 RDB 文件格式相同) │
├─────────────────────────────────────┤
│ 增量 AOF 命令 │ ← 记录重写期间的新写入
│ (增量 SET key1 val1 ...) │
└─────────────────────────────────────┘
# 开启混合持久化
redis-cli CONFIG SET aof-use-rdb-preamble yes
# 恢复时:先加载 RDB 部分(快),再重放 AOF 部分(少)
持久化方案对比
| 方案 | 数据安全 | 恢复速度 | 文件大小 | 推荐场景 |
|---|---|---|---|---|
| 纯 RDB | 中 | 快 | 小 | 数据允许少量丢失 |
| 纯 AOF | 高 | 慢 | 大 | 对数据安全要求极高 |
| RDB + AOF | 高 | 快 | 中 | 生产环境推荐 |
| 关闭持久化 | 无 | N/A | N/A | 纯缓存场景 |
💡 技巧:生产环境推荐同时开启 RDB 和 AOF,AOF 保证数据安全,RDB 用于冷备和灾难恢复。
9.5 fork 性能优化
BGSAVE 和 BGREWRITEAOF 都会 fork 子进程,fork 操作可能阻塞主进程。
fork 耗时因素
| 因素 | 影响 |
|---|---|
| 内存越大 | fork 越慢(需复制页表) |
| Linux 版本 | 较新版本 fork 更快 |
| 虚拟化环境 | fork 可能比物理机慢 |
# 查看最近一次 fork 耗时
redis-cli INFO stats | grep latest_fork_usec
# latest_fork_usec:1500 ← 1.5 毫秒
# fork 耗时参考
# 1 GB 内存: 约 20-30ms
# 10 GB 内存: 约 200-300ms
# 25 GB 内存: 约 500ms+
优化建议
# 1. 控制 Redis 实例内存(建议单实例不超过 10-15 GB)
maxmemory 10gb
# 2. 避免在高峰期执行 BGSAVE
# 可以通过脚本控制触发时间
# 3. 使用 Linux 大页内存时注意
# 大页内存会导致 fork 时 COW(Copy-On-Write)复制更多数据
# 建议关闭大页内存
echo never > /sys/kernel/mm/transparent_hugepage/enabled
# 4. AOF 重写期间暂停 fsync(减少 I/O 压力)
no-appendfsync-on-rewrite yes # 但有数据丢失风险
# 5. 控制 AOF 重写触发频率
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 256mb # 增大最小触发大小
# 6. 增量 fsync(Redis 3.2+)
rdb-save-incremental-fsync yes
aof-rewrite-incremental-fsync yes
9.6 备份策略
自动备份脚本
#!/bin/bash
# redis_backup.sh - Redis 自动备份脚本
BACKUP_DIR="/backup/redis"
REDIS_CLI="redis-cli"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=7
# 创建备份目录
mkdir -p ${BACKUP_DIR}
# 触发 RDB 快照
${REDIS_CLI} BGSAVE
# 等待 BGSAVE 完成
while [ "$(${REDIS_CLI} LASTSAVE)" == "$LAST_SAVE" ]; do
sleep 1
done
# 复制 RDB 文件
cp /var/lib/redis/dump.rdb ${BACKUP_DIR}/dump_${DATE}.rdb
# 压缩
gzip ${BACKUP_DIR}/dump_${DATE}.rdb
# 删除过期备份
find ${BACKUP_DIR} -name "*.rdb.gz" -mtime +${KEEP_DAYS} -delete
echo "[$(date)] Backup completed: dump_${DATE}.rdb.gz"
定时任务
# crontab -e
# 每小时备份一次
0 * * * * /opt/scripts/redis_backup.sh >> /var/log/redis_backup.log 2>&1
📌 业务场景
场景一:电商缓存持久化
# 电商缓存场景,允许少量数据丢失
save 900 1
save 300 100
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes
maxmemory 8gb
场景二:金融数据持久化
# 金融场景,数据零丢失
save 60 1000
appendonly yes
appendfsync always # 每次写入都同步(性能较差,但最安全)
aof-use-rdb-preamble yes
场景三:纯缓存场景
# 纯缓存,不需要持久化
save ""
appendonly no
maxmemory 16gb
maxmemory-policy allkeys-lru