redis持久化
redis持久化
Redis 提供了3种持久化机制来保证数据在断电或重启后不会丢失:
- 快照(snapshotting,RDB): RDB 以指定的时间间隔执行数据集的时间点快照。
- 只追加文件(append-only file, AOF): AOF 持久性记录服务器接收到的每个写操作。然后可以在服务器启动时再次重播这些操作,从而重建原始数据集。命令使用与 Redis 协议本身相同的格式进行记录。
- RDB 和 AOF 的混合持久化(Redis 4.0 新增)
RDB
怎么运行
Redis 提供了两个命令来生成 RDB 快照文件:
save
: 同步保存操作,会阻塞 Redis 主线程;bgsave
: fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项。
每当 Redis 需要将数据集转储到磁盘时,就会发生以下情况:
- Redis fork子进程。我们现在有一个子进程和一个父进程。
- 子进程开始将数据集写入临时 RDB 文件。
- 当子进程写完新的 RDB 文件后,它会替换旧的 RDB 文件(Copy-on-Write)。
优点
- RDB 文件非常适合备份。例如,您可能希望在最近 24 小时内每小时归档一次 RDB 文件,并在 30 天内每天保存一个 RDB 快照。这使您可以在发生灾难时轻松恢复不同版本的数据集。
- RDB 非常适合灾难恢复,它是一个紧凑的文件,可以传输到远程数据中心或 Amazon S3(可能是加密的)。
- RDB 最大限度地提高了 Redis 的性能,因为 Redis 父进程为了持久化需要做的唯一工作就是派生一个子进程,该子进程将完成其余所有工作。父进程永远不会执行磁盘 I/O 或类似操作。
- 与 AOF 相比,RDB 允许更快地重新启动大数据集。
缺点
- 如果您需要在 Redis 停止工作(例如断电后)时最大程度地减少数据丢失的可能性,那么 RDB 并不好。您可以在生成 RDB 的位置配置不同的保存点(例如,在至少五分钟并对数据集进行 100 次写入后)。但是,您通常会每五分钟或更长时间创建一个 RDB 快照,因此,如果 Redis 由于任何原因在没有正确关闭的情况下停止工作,您应该做好丢失最新分钟数据的准备。
- RDB 需要经常 fork() 才能使用子进程持久保存在磁盘上。如果数据集很大,fork() 可能会非常耗时,并且如果数据集很大并且 CPU 性能不是很好,可能会导致 Redis 停止为客户端提供服务几毫秒甚至一秒。AOF 还需要 fork() 但频率较低,您可以调整重写日志的频率,而无需牺牲持久性。
AOF
怎么运行
Redis 6.0 之后已经默认是开启了AOF,每次 Redis 收到更改数据集的命令(例如SET
)时,它都会将其追加到 AOF 中。当您重新启动 Redis 时,它将重放 AOF 以重建状态。
从Redis 7.0.0开始,Redis使用多部分AOF机制。即把原来的单个AOF文件拆分为基础文件(最多一个)和增量文件(可能不止一个)。基础文件表示重写 AOF 时存在的数据的初始(RDB 或 AOF 格式)快照。增量文件包含自上次创建基本 AOF 文件以来的增量更改。所有这些文件都放在单独的目录中,并由清单文件跟踪。
命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用write
函数(系统调用),write
将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。
文件同步(fsync):AOF 缓冲区根据对应的持久化方式( fsync
策略)向硬盘做同步操作。这一步需要调用 fsync
函数(系统调用), fsync
针对单个文件操作,对其进行强制硬盘同步,fsync
将阻塞直到写入磁盘完成后返回,保证了数据持久化。
文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
日志重写
随着写操作的执行,AOF 会变得越来越大。例如,如果您将计数器递增 100 次,则最终数据集中会出现一个包含最终值的键,但 AOF 中会出现 100 个条目。其中 99 个条目不需要重建当前状态。
重写是完全安全的。当 Redis 继续追加到旧文件时,会使用创建当前数据集所需的最少操作集生成一个全新的文件,一旦第二个文件准备就绪,Redis 就会切换这两个文件并开始追加到新文件。
因此,Redis 支持一个有趣的功能:它能够在后台重建 AOF,而不会中断对客户端的服务。每当您发出 时BGREWRITEAOF
,Redis 都会写入在内存中重建当前数据集所需的最短命令序列。如果您将 AOF 与 Redis 2.2 一起使用,则需要BGREWRITEAOF
不时运行。由于Redis 2.4能够自动触发日志重写(更多信息请参见示例配置文件)。
**从Redis 7.0.0开始,当计划进行AOF重写时,Redis父进程会打开一个新的增量AOF文件来继续写入。子进程执行重写逻辑并生成新的基础 AOF。**Redis将使用临时清单文件来跟踪新生成的基础文件和增量文件。当它们准备好时,Redis将执行原子替换操作以使这个临时清单文件生效。为了避免 AOF 重写重复失败和重试时创建大量增量文件的问题,Redis 引入了 AOF 重写限制机制,以确保失败的 AOF 重写以越来越慢的速度重试。
Redis >= 7.0
- Redis fork子进程,所以现在我们有一个子进程和一个父进程。
- 子进程开始在临时文件中写入新的基本 AOF。
- 父级打开一个新的增量 AOF 文件以继续写入更新。如果重写失败,旧的基本文件和增量文件(如果有)加上这个新打开的增量文件就代表了完整的更新数据集,所以我们是安全的。
- 当子进程完成重写基本文件时,父进程收到一个信号,并使用新打开的增量文件和子进程生成的基本文件来构建临时清单,并将其保留。
- Redis 对清单文件进行原子交换,以便 AOF 重写的结果生效。Redis 还会清理旧的基本文件和任何未使用的增量文件。
Redis < 7.0
- Redisfork子进程,所以现在我们有一个子进程和一个父进程。
- 子进程开始在临时文件中写入新的 AOF。
- 父级将所有新更改累积在内存缓冲区中(但同时它将新更改写入旧的仅追加文件中,因此如果重写失败,我们是安全的)。
- 当子进程重写完文件后,父进程会收到一个信号,并将内存缓冲区附加到子进程生成的文件的末尾。
- 现在,Redis 以原子方式将新文件重命名为旧文件,并开始将新数据附加到新文件中。
优点
- 您可以有不同的 fsync 策略:根本不进行 fsync、每秒进行 fsync、每次查询时进行 fsync。采用每秒fsync的默认策略,写入性能仍然很棒。fsync 是使用后台线程执行的,当没有 fsync 正在进行时,主线程将努力执行写入,因此您只能丢失一秒钟的写入。
- AOF 日志是仅追加日志,因此不会出现查找问题,并且在断电时也不会出现损坏问题。即使由于某种原因(磁盘已满或其他原因)日志以半写命令结束,redis-check-aof 工具也能够轻松修复它。
- 当 AOF 太大时,Redis 能够在后台自动重写 AOF。重写是完全安全的,因为当 Redis 继续追加到旧文件时,会使用创建当前数据集所需的最少操作集生成一个全新的文件,一旦第二个文件准备就绪,Redis 就会切换这两个文件并开始追加到新的那一个。
- AOF 以一种易于理解和解析的格式依次包含所有操作的日志。您甚至可以轻松导出 AOF 文件。例如,即使您不小心使用该
FLUSHALL
命令刷新了所有内容,只要在此期间没有执行日志重写,您仍然可以通过停止服务器、删除最新命令并再次重新启动 Redis 来保存数据集。
缺点
- 对于相同的数据集,AOF 文件通常比等效的 RDB 文件大。
- AOF 可能比 RDB 慢,具体取决于确切的 fsync 策略。一般来说,将 fsync 设置为每秒一次的性能仍然非常高,并且禁用 fsync 后,即使在高负载下,它也应该与 RDB 一样快。即使在巨大的写入负载的情况下,RDB仍然能够对最大延迟提供更多的保证。
Redis < 7.0
- 如果在重写期间对数据库进行写入(这些内容会缓冲在内存中并在最后写入新的 AOF),则 AOF 可能会使用大量内存。
- 重写期间到达的所有写入命令都会写入磁盘两次。
- Redis 在重写结束时冻结写入并将这些写入命令同步到新的 AOF 文件。因此可能会导致一段时间内的数据持久化能力下降。
混合持久化
Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。
这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
从持久化文件恢复数据
那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。