*Redis 持久化指南

持久化指将数据写入持久存储(如固态硬盘 SSD)。Redis 提供一系列持久化选项:

  • RDB(Redis Database):在指定间隔执行数据集的时间点快照
  • AOF(Append Only File):记录服务器接收的每个写操作,服务器启动时重放这些操作以重建原始数据集
  • 无持久化:完全禁用持久化,有时用于缓存场景
  • RDB + AOF:在同一实例中结合使用两者

*RDB 的优势

  • RDB 是非常紧凑的单文件时间点表示,非常适合备份。可每小时归档最近 24 小时的 RDB 文件,每天保存 30 天的快照
  • RDB 非常适合灾难恢复,是单个紧凑文件,可传输到远程数据中心或 Amazon S3(可加密)
  • RDB 最大化 Redis 性能,因为持久化所需的工作只是 fork 一个子进程完成其余工作,父进程从不执行磁盘 I/O
  • RDB 允许大数据集比 AOF 更快重启
  • 在副本上,RDB 支持重启和故障转移后的部分重新同步

*RDB 的劣势

  • RDB 不适合需要最小化数据丢失风险的场景。通常每 5 分钟或更长时间创建一次 RDB 快照,如果 Redis 停止工作,可能丢失最近几分钟的数据
  • RDB 需要频繁 fork() 以使用子进程持久化到磁盘。如果数据集很大,fork() 可能耗时,导致 Redis 停止服务客户端几毫秒甚至一秒

*AOF 的优势

  • AOF 更持久:可配置不同的 fsync 策略:无 fsync、每秒 fsync、每次查询 fsync。默认每秒 fsync 策略下写入性能仍然很好。使用后台线程执行 fsync,主线程努力在 fsync 未进行时执行写入,最多丢失一秒的写入
  • AOF 日志是追加日志,没有寻道操作,断电也不会损坏。即使日志以半写命令结尾,redis-check-aof 工具也能轻松修复
  • Redis 能够在 AOF 过大时在后台自动重写。重写完全安全:Redis 继续追加到旧文件的同时,生成包含创建当前数据集所需最小操作集的全新文件,完成后切换两个文件并开始追加到新文件
  • AOF 包含所有操作的日志,格式易于理解和解析。即使意外执行了 FLUSHALL,只要未执行重写,停止服务器、删除最后一条命令并重启 Redis 即可恢复数据

*AOF 的劣势

  • AOF 文件通常比同等数据集的 RDB 文件大
  • AOF 可能比 RDB 慢,取决于确切的 fsync 策略。通常每秒 fsync 性能仍然很高,禁用 fsync 时与 RDB 一样快

Redis < 7.0

  • 重写期间如果有写入,AOF 可能使用大量内存(在内存中缓冲并写入新 AOF)
  • 重写期间到达的所有写入命令会被写入磁盘两次
  • Redis 可能在重写结束时冻结写入和 fsync

*应该选择哪种持久化方式?

  • 如果需要与 PostgreSQL 相当的数据安全性,应同时使用两种持久化方法
  • 如果非常关心数据但可以接受灾难中丢失几分钟数据,可以仅使用 RDB
  • 许多用户仅使用 AOF,但不推荐,因为偶尔进行 RDB 快照对数据库备份、更快重启以及 AOF 引擎出现 bug 时很有用

*快照(Snapshotting)

默认情况下 Redis 将数据集快照保存在磁盘上的二进制文件 dump.rdb 中。可配置 Redis 在数据集至少有 M 次更改时每 N 秒保存一次,或手动调用 SAVEBGSAVE 命令。

示例:每 60 秒如果至少 1000 个键更改则自动转储:

save 60 1000

*工作原理

每当 Redis 需要将数据集转储到磁盘时:

  1. Redis fork,产生子进程和父进程
  2. 子进程开始将数据集写入临时 RDB 文件
  3. 子进程完成写入后,用新文件替换旧文件

此方法使 Redis 受益于写时复制(copy-on-write)语义。

*追加文件(Append-Only File)

快照不够持久。如果运行 Redis 的计算机停止、电源线故障或意外 kill -9 实例,最新写入 Redis 的数据将丢失。

AOF 是 Redis 的替代完全持久策略,自 1.1 版本起可用。

在配置文件中启用:

appendonly yes

自 Redis 7.0.0 起,Redis 使用多部分 AOF 机制。原始单 AOF 文件被拆分为基础文件(最多一个)和增量文件(可能有多个)。所有文件放在单独目录中,由清单文件跟踪。

*日志重写

AOF 随写操作执行而越来越大。例如递增计数器 100 次,数据集中只有一个键包含最终值,但 AOF 中有 100 条条目,其中 99 条不需要重建当前状态。

重写完全安全:Redis 继续追加到旧文件的同时,生成包含创建当前数据集所需最小操作集的全新文件,完成后切换并开始追加到新文件。

Redis >= 7.0

  • Redis fork,产生子进程和父进程
  • 子进程开始在临时文件中写入新基础 AOF
  • 父进程打开新的增量 AOF 文件继续写入更新
  • 子进程完成重写基础文件后,父进程使用新打开的增量文件和子进程生成的基础文件构建临时清单并持久化
  • Redis 执行清单文件的原子交换,使重写结果生效,并清理旧基础文件和未使用的增量文件

Redis < 7.0

  • Redis fork
  • 子进程开始在临时文件中写入新 AOF
  • 父进程将所有新更改累积在内存缓冲区中(同时写入旧追加文件,如果重写失败则安全)
  • 子进程完成后,父进程将内存缓冲区追加到子进程生成的文件末尾
  • Redis 原子地将新文件重命名为旧文件,并开始追加到新文件

*AOF 的持久性如何?

可配置 Redis fsync 数据到磁盘的频率:

  • appendfsync always:每次新命令追加到 AOF 时 fsync。非常慢,非常安全
  • appendfsync everysec:每秒 fsync。足够快(自 2.4 版本起可能与快照一样快),灾难中可能丢失 1 秒数据
  • appendfsync no:从不 fsync,将数据交给操作系统。最快但最不安全。Linux 通常每 30 秒刷新数据

建议(默认)策略是每秒 fsync,既快又相对安全。

*AOF 文件被截断怎么办?

服务器在写入 AOF 文件时可能崩溃,或写入时卷已满。最新主要版本的 Redis 仍能加载 AOF,仅丢弃文件中最后一个格式不正确的命令。

旧版本 Redis 可能需要以下步骤:

  1. 备份 AOF 文件
  2. 使用 redis-check-aof --fix <filename> 修复
  3. 使用 diff -u 检查差异
  4. 用修复后的文件重启服务器

*AOF 文件损坏怎么办?

如果 AOF 文件不仅被截断,中间还有无效字节序列,Redis 将在启动时报错并中止。

最佳做法是先运行 redis-check-aof 工具(不加 --fix 选项)了解问题,跳到给定偏移量查看是否能手动修复。AOF 使用与 Redis 协议相同的格式,相当简单。否则可让工具修复,但可能从无效部分到文件末尾的所有 AOF 部分被丢弃,导致大量数据丢失。

*从 RDB 切换到 AOF

重要:不遵循此程序(例如直接更改配置并重启服务器)可能导致数据丢失!

Redis >= 2.2

  1. 备份最新的 dump.rdb 文件
  2. 在实时数据库上启用 AOF:redis-cli config set appendonly yes
  3. 可选禁用 RDB:redis-cli config set save ""
  4. 确保写入正确追加到追加文件
  5. 重要:更新 redis.conf(可能通过 CONFIG REWRITE),确保与上述配置匹配
  6. 重启前等待 AOF 重写完成持久化数据。观察 INFO persistence,等待 aof_rewrite_in_progressaof_rewrite_scheduled 为 0,验证 aof_last_bgrewrite_status 为 ok
  7. 重启后检查数据库包含与之前相同数量的键

*AOF 和 RDB 持久化的交互

Redis >= 2.4 确保避免在 RDB 快照操作已在进行时触发 AOF 重写,或允许 AOF 重写正在进行时执行 BGSAVE。这防止两个 Redis 后台进程同时执行大量磁盘 I/O。

如果同时启用 AOF 和 RDB 持久化且 Redis 重启,将使用 AOF 文件重建原始数据集,因为它保证是最完整的。

*备份 Redis 数据

务必备份数据库。磁盘损坏、云实例消失等:没有备份意味着数据消失的巨大风险。

Redis 非常数据备份友好,因为可以在数据库运行时复制 RDB 文件:RDB 一旦生成就不再修改,生成时使用临时名称,新快照完成后才原子地重命名为最终目标。

建议:

  • 在服务器中创建 cron 作业,每小时在一个目录中创建 RDB 文件快照,每天在另一个目录中创建快照
  • 每次 cron 脚本运行时,调用 find 命令删除过旧的快照:例如保留最近 48 小时的每小时快照,一到两个月的每日快照
  • 每天至少一次将 RDB 快照传输到数据中心外部或至少传输到运行 Redis 实例的物理机器外部

*备份 AOF 持久化

自 Redis 7.0.0 起,AOF 文件拆分为多个文件,位于 appenddirname 配置确定的单个目录中。正常操作期间只需复制/tar 此目录中的文件即可备份。但如果在重写期间执行此操作,可能得到无效备份。

解决方法:

  1. 关闭自动重写:CONFIG SET auto-aof-rewrite-percentage 0
  2. 检查无当前重写在进行:INFO persistence,验证 aof_rewrite_in_progress 为 0
  3. 安全复制 appenddirname 目录中的文件
  4. 完成后重新启用重写:CONFIG SET auto-aof-rewrite-percentage <prev-value>

注意:要最小化禁用 AOF 重写的时间,可在步骤 3 中对 appenddirname 中的文件创建硬链接,然后重新启用重写。现在可以复制/tar 硬链接并在完成后删除。

7.0.0 之前的版本只需像备份 RDB 快照一样复制 aof 文件。

*灾难恢复

灾难恢复基本上是备份的故事,加上将这些备份传输到许多不同外部数据中心的能力。

  • Amazon S3 等类似服务是实现灾难恢复系统的好方法。以加密形式将每日或每小时 RDB 快照传输到 S3。可使用 gpg -c 加密数据
  • 使用 SCP 传输快照到远程服务器。获取远离你的位置的小型 VPS,安装 ssh,生成无密码短语的 ssh 客户端密钥,添加到 VPS 的 authorized_keys 文件
  • 使用至少两个不同提供商的 VPS 以获得最佳效果

确保传输完成后能够验证文件大小(应与复制的文件大小匹配)以及可能的 SHA1 摘要。