Redis持久化机制深度解析

发表时间: 2024-05-28 13:01

前言

Redis的数据全部存储在内存,如果机器突然宕机,那么数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。Redis为我们提供了两种持久化方案,一种是基于快照RDB(Redis DataBase),另外一种是基于 AOF (Append Only File)日志 。Redis也可以同时支持 AOF 持久化和 RDB 持久化。在这种情况下,当 AOF 重启时,会优先使用 AOF 文件去恢复原始数据。因为 AOF 中保存的数据通常比 RDB 中保存的数据更加完整。


一、AOF(Append Only File)

AOF :Redis 默认不开启。它的目的是为了解决生成RDB后数据不能实时一致的问题,所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

Redis每次执行写指令时都会向AOF文件中添加一条日志,但新的记录不会立即写入磁盘(fsync)而是缓存在写入缓冲区中。

我们可以配置将缓冲区中的数据写入磁盘的策略,避免数据丢失。

将缓冲区中数据写入磁盘是一个耗时操作,频繁写磁盘会对性能造成影响但是Redis崩溃丢失数据也较少,因此我们需要根据应用场景进行权衡。

日志重写

AOF中可能会记录多余的指令,若我们对同一个key执行了100次set指令, AOF文件中就会有100条记录但只仅保留最后一条set指令即可恢复数据。AOF重写会整理AOF文件清理不必要的指令日志(如删除被覆盖的set指令),减少AOF文件大小。

redis 采用后台重写的策略,即 fork 一个子进程把整理后的 AOF 写入到临时文件中。使用BGREWRITEAOF可以手动触发后台重写操作。

实际上AOF重写不会读取原来的AOF文件,子进程会带有一份当前数据的副本,并根据该副本直接生成新的AOF文件。

主进程在重写期间将新增的写操作写入原来的 AOF文件 和 AOF重写缓存 中,即使重写失败原来的AOF文件仍保存了完整的数据。当子进程完成AOF重写后会向主进程发送信号,主进程收到该信号后会将AOF重写缓存中的内容写入新的AOF文件,然后用新的AOF文件覆盖原有文件。

下面来看看重写的一个流程图:

对于上图有四个关键点补充一下:

  1. 在重写期间,由于主进程依然在响应命令,为了保证最终备份的完整性;因此它依然会写入旧的AOF file中,如果重写失败,能够保证数据不丢失。
  2. 为了把重写期间响应的写入信息也写入到新的文件中,因此也会为子进程保留一个buf,防止新写的file丢失数据。
  3. 重写是直接把当前内存的数据生成对应命令,并不需要读取老的AOF文件进行分析、命令合并。
  4. AOF文件直接采用的文本协议,主要是兼容性好、追加方便、可读性高可认为修改修复。

AOF的配置

在redis.conf中配置AOF:

# 是否开启AOF,默认关闭(no)  appendonly yes    #  AOF 文件名  appendfilename appendonly.aof    # AOF 文件写入策略# appendfsync always # 每次收到写命令就立即强制写入磁盘, 一致性最好但性能最差appendfsync everysec # 每秒钟写入磁盘一次, 平衡一致性和性能,推荐配置# appendfsync no     # 依赖操作系统的写入策略,一般为30秒左右一次,性能最好但是一致性最没有保证    # 在 AOF 重写期间将 appendfsync 配置为 no, # 默认配置为 no 避免因为磁盘IO性能不足,导致重写时间过长。若重写期间 redis 崩溃则可能丢失数据。# 配置为 yes 则可以保证一致性,但会延长重写耗时no-appendfsync-on-rewrite no     # AOF文件新增大小达到上次重写后文件大小的 100%,则自动进行AOF重写。# 配置为 0 会禁用自动重写  auto-aof-rewrite-percentage 100    # 当前AOF文件超过64mb才会自动进行AOF重写auto-aof-rewrite-min-size 64mb

Redis会记录启动时或上次重写后AOF文件的大小,若新增数据的大小达到原大小的100%(auto-aof-rewrite-percentage配置)则触发重写。

只有当前AOF文件体积大于auto-aof-rewrite-min-size时才会执行重写操作,否则即使新增数据量超过指定百分比也不会执行重写。这样避免了原文件过小导致初期频繁重写的问题。

工作原理

AOF 日志记录 Redis 的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。

命令追加 当 AOF 持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。

文件写入和同步 关于何时将 aof_buf 缓冲区的内容写入 AOF 文件中,Redis 提供了三种写回策略:

  • Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
  • Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
  • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。


AOF 的优缺点

优点:数据的完整性和一致性更高。

缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。


二、RDB(Redis DataBase)

RDB 是 Redis 默认的持久化方案。RDB通过快照的形式将数据保存到磁盘中。所谓快照,可以理解为在某一时间点将数据集的一个快照。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。

RDB的配置

# 配置rdb文件的路径# 若使用相对路径,工作目录由 dir 配置指定dbfilename dump.rdbdir /var/lib/redis# save 项配置保存策略, 格式为`save <seconds> <changes>`# save 条件可以配置多个, 若其中任一个条件被满足则会更新快照save 900 1 # 900 秒内至少有 1 个 key 被改变 save 300 10 # 300 秒内至少有 300 个 key 被改变 save 60 10000 # 60 秒内至少有 10000 个 key 被改变save "" # 注释所有 save 配置,或在最后添加一个空的save配置可以禁用自动保存stop-writes-on-bgsave-error yes # 当保存快照文件失败后,redis不再执行任何写请求,以避免无法持久化造成错误rdbcompression yes # 在保存快照时对文件进行压缩rdbchecksum yes # 在快照文件结尾添加一个CRC64校验和;若该配置项为no则会写入0作为校验和,表示redis重新启动时无需进行校验

RDB的原理

在Redis中RDB持久化的触发分为两种:自己手动触发与Redis定时触发。

针对RDB方式的持久化,手动触发可以使用:

  • save:会阻塞当前Redis服务器,直到持久化完成,线上应该禁止使用。
  • bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。

而自动触发的场景主要是有以下几点:

  • 根据我们的 save m n 配置规则自动触发;
  • 从节点全量复制时,主节点发送rdb文件给从节点完成复制操作,主节点会触发 bgsave;
  • 执行 debug reload 时;
  • 执行 shutdown时,如果没有开启aof,也会触发。

由于 save 基本不会被使用到,我们重点看看 bgsave 这个命令是如何完成RDB的持久化的。

这里注意的是 fork 操作会阻塞,导致Redis读写性能下降。我们可以控制单个Redis实例的最大内存,来尽可能降低Redis在fork时的事件消耗。以及上面提到的自动触发的频率减少fork次数,或者使用手动触发,根据自己的机制来完成持久化。

RDB 的优缺点

优点:

  • 适合大规模的数据恢复,一般建议Redis单实例小于10GB。
  • 如果追求系统的高可用和数据的高要求,RDB并不是很好的选择。

缺点:

  • 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
  • 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件,不过这个问题在最新版的Redis已经优化为异步任务。


三、数据恢复

当 AOF 重启时,会优先使用 AOF 文件去恢复原始数据。因为 AOF 中保存的数据通常比 RDB 中保存的数据更加完整。

启动Redis时会先检查AOF文件是否存在,如果不存在就尝试加载RDB。那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。


四、持久化方案的建议

RDB 和 AOF 混合方式(4.0 版本),内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。

通常,如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。如果你可以接受灾难带来的几分钟的数据丢失,那么你可以仅使用 RDB。

很多仅使用了 AOF,但是建议,既然 RDB 可以时不时的给数据做个完整的快照,并且提供更快的重启,所以最好还是也使用 RDB。

目前,通常的设计思路是利用复制(Replication)机制来弥补 AOF、snapshot 性能上的不足,达到了数据可持久化。即 Master 上 Snapshot 和 AOF 都不做,来保证 Master 的读写性能,而 Slave 上则同时开启 Snapshot 和 AOF 来进行持久化,保证数据的安全性。