面试题:redis的持久化⽅式有哪些?AOF和RDB如何实现?AOF的重写机制是什么?

面试官:redis的持久化⽅式有哪些?他们分别是怎么实现的?有什么区别?

Redis提供了几种持久化方式来确保数据的持久性,即使在服务器重启或崩溃的情况下也能恢复数据。以下是Redis主要的持久化方式及其区别:

一、RDB(Redis Database)快照

实现方式:通过创建Redis数据集在某一时间点的快照(snapshot)来实现持久化。Redis会在指定的时间间隔内生成数据集的快照,并将其保存到磁盘。

RDB快照的触发条件

RDB快照可以通过以下两种方式触发:

手动触发

  1. SAVE 命令:当执行 SAVE 命令时,Redis会阻塞主线程,将当前内存中的所有数据写入到一个RDB文件中。这种方式在数据量大时会导致Redis服务暂停,因此不推荐在生产环境中使用。
  2. BGSAVE 命令: BGSAVE 命令会创建一个子进程来执行快照操作,而主进程则继续处理客户端的请求。这种方式不会阻塞主线程,因此更加适用于生产环境。

自动触发

  1. 通过Redis配置文件中的 save 选项来配置自动快照的条件。例如,可以配置为“在900秒内至少有1次写操作”时触发快照,或者“在300秒内至少有10次写操作”时触发快照等。当满足这些条件时,Redis会自动执行 BGSAVE 命令来生成RDB快照。
  2. 
    save 900 1    # 900 秒(15 分钟)内至少有 1 次写操作save 300 10   # 300 秒(5 分钟)内至少有 10 次写操作save 60 10000 # 60 秒内至少有 10000 次写操作
    

RDB快照的生成过程

RDB快照的生成过程主要包括以下几个步骤:

  1. 创建子进程:当执行 BGSAVE 命令或满足自动快照条件时,Redis会调用操作系统的 fork() 函数来创建一个子进程。这个子进程会继承父进程的内存数据(通过写时复制机制),并且拥有自己独立的地址空间。

  2. 执行快照操作:子进程创建成功后,会开始遍历内存中的数据结构,并将它们序列化到一个临时的RDB文件中。这个过程中,父进程仍然可以继续处理客户端的请求,并且这些请求产生的数据变更会通过写时复制机制被记录在父进程的内存中,而不会影响到子进程的RDB文件生成。

  3. 替换RDB文件:当子进程完成RDB文件的生成后,它会将临时文件重命名为最终的RDB文件名(如 dump.rdb),并覆盖之前的RDB文件(如果存在的话)。

这面这张图描绘了RDB快照生成的详细过程

RDB生成的写时复制机制在RDB快照生成过程中,写时复制机制起到了关键作用。写时复制(Copy-On-Write,COW)是一种优化技术,它允许父子进程共享相同的物理内存页,直到父进程或子进程尝试修改这些页为止。当发生修改时,操作系统会为修改者创建一个新的内存页副本,并将修改应用到这个副本上。这样,父进程和子进程就可以各自独立地操作自己的内存数据,而不会相互干扰。

RDB持久化优点

  1. 启动速度快:由于是保存的整个数据库快照,恢复时直接加载快照文件即可,恢复速度较快。
  2. 适合备份:可以方便地将RDB文件复制到远程服务器,实现数据的备份和恢复。

RDB持久化缺点

  1. 数据丢失风险:由于RDB是在指定时间间隔内进行的,如果Redis在最近一次快照之后发生故障,可能会丢失这段时间内的数据变化。生成耗时较长:生成快照过程会占用一定的CPU和IO资源,尤其是在数据量较大时,可能导致性能下降。

二、AOF(Append-Only File)日志

Redis的AOF(Append-Only File)日志是其持久化机制中的一种,主要通过记录Redis的所有写操作来确保数据的持久性。以下是Redis AOF日志实现的详细过程:

AOF日志的写入方式

  1. 追加写入

    • 当Redis接收到一个写命令时,会先将这个命令写入到内存中的缓冲区中,并将这个命令追加到AOF文件的末尾。这种写入方式是追加写(append-only),而不是覆盖写(overwrite),从而保证了即使出现写操作中途失败或Redis被意外关闭,这些操作仍然可以通过重新加载AOF文件来恢复。
  2. 写入策略 Redis的AOF写入策略是指决定何时将内存中的写操作同步到AOF文件中的策略。Redis提供了三种不同的AOF写入策略,这些策略可以通过配置项 appendfsync 来选择,以满足不同场景下的数据持久化和性能需求。以下是关于Redis AOF写入策略的详细解释:

### Always策略

### Everysec(每秒)策略

### No(操作系统控制)策略

  • 描述 每个写命令执行完后,先把日志写到AOF文件的内存缓冲区,然后由操作系统决定何时将缓冲区内容写回磁盘。

  • 优点 性能最好,因为Redis不需要关心磁盘写入的时机,完全由操作系统来控制。

  • 缺点 数据安全性最低,因为操作系统可能在缓冲区刷新到磁盘之前出现故障,导致数据丢失。此外,由于写入时机不可控,所以在宕机后可能会丢失较多的数据。

  • 描述 每个写命令执行完后,先把日志写到AOF文件的内存缓冲区,然后每隔一秒把缓冲区中的内容写入磁盘。

  • 优点 在数据安全性和性能之间取得了平衡。每秒一次的写入频率既保证了数据的安全性(在宕机时最多丢失一秒的数据),又避免了频繁的磁盘写入操作对性能的影响。

  • 缺点 在系统突然宕机的情况下,可能会丢失上一秒内未落盘的命令操作。

  • 描述 每个写命令执行完后,立即同步地将日志写回磁盘。

  • 优点 数据安全性最高,因为每次写操作都会立即持久化到磁盘,几乎不会丢失数据。

  • 缺点 性能开销最大,因为每次写操作都需要进行磁盘写入操作,这可能会阻塞Redis的主线程,影响处理速度。

恢复过程当Redis服务器重启时,会首先加载AOF日志文件。然后,依次执行AOF日志文件中记录的每一条写操作,以重建Redis数据库。AOF日志的恢复速度相对较慢,因为需要逐条执行文件中的命令。但随着Redis版本的优化,AOF的恢复速度也在不断提高。

AOF持久化优点

  1. 更少的数据丢失风险:AOF可以通过配置来实现高频率的持久化,比如每秒调用一次,即使Redis崩溃,也最多丢失1秒的数据。日志格式易读:AOF文件是以追加的方式记录命令的,格式简单清晰,便于分析和修改。

AOF持久化缺点:

  1. 文件体积较大:由于是记录每个写操作,AOF文件的大小通常会比RDB文件大,特别是当写操作频繁时。恢复速度较慢:因为恢复时需要逐条执行AOF文件中的命令,相对于加载RDB快照文件,速度较慢。性能影响:频繁的磁盘写操作可能对Redis的运行性能产生一定影响。

RDB快照方式适用于对数据完整性要求不高但需要快速恢复的场景,适合做数据备份。AOF日志方式适用于对数据完整性要求较高的场景,通过频繁同步日志文件来保证数据的完整性。

面试官:AOF持久化会记录每一次写入操作,这会导致什么问题,该如何解决?

AOF持久化机制通过记录Redis的所有写操作来保证数据的安全性。然而,随着时间的推移,AOF文件可能会变得越来越大,存在性能问题。

这里的性能问题主要在于以下三个方面:一是,文件系统本身对文件大小有限制,无法保存过大的文件;二是,如果文件太大,之后再往里面追加命令记录的话,效率也会变低;三是,如果发生宕机,AOF 中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程就会非常缓慢,影响到 Redis 正常使用。

为了解决这个问题,Redis引入了AOF重写机制。

一、AOF重写机制的原理

AOF重写机制的原理是根据Redis进程内的当前数据状态生成一个新的AOF文件,这个新文件只包含恢复当前数据状态所必需的命令,而不是历史上所有的写入命令。通过这种方式,可以删除AOF文件中的冗余和无效命令,从而减小文件的大小并提高数据恢复的效率。

二、AOF重写机制的流程

  1. 触发重写

    • AOF重写可以通过手动触发或自动配置触发。手动触发是通过执行BGREWRITEAOF命令来请求Redis在后台进行AOF重写。自动触发是在Redis配置文件中设置auto-aof-rewrite-percentage和auto-aof-rewrite-min-size参数,当AOF文件的大小超过auto-aof-rewrite-min-size指定的最小大小时,并且AOF文件的大小比上一次重写后的大小增长了auto-aof-rewrite-percentage指定的百分比时,Redis会自动触发重写。
  2. 后台重写

    • 当Redis收到重写请求时,它会启动一个新的子进程来执行重写操作,而主进程则继续处理客户端的请求。这个过程中,操作系统使用了写时复制(Copy-On-Write)技术,使得子进程可以共享主进程的内存数据,而不需要完全复制一份。
  3. 创建新的AOF文件

    • 子进程会读取当前内存中的数据库状态,并将其转换为一系列的Redis命令。然后,这些命令会被写入到一个新的AOF文件中。这个新的AOF文件只包含恢复当前数据状态所必需的命令,因此它的大小会比原来的AOF文件小很多。
  4. 替换旧的AOF文件

    • 当新的AOF文件创建完毕后,子进程会发送一个信号给主进程,告诉它重写操作已经完成。然后主进程会使用新的AOF文件来替换旧的AOF文件,并继续将后续的写命令追加到新的AOF文件中。

三、AOF重写时如果写入新数据怎么办

在AOF重写期间,Redis会创建一个AOF重写缓冲区。这个缓冲区的作用是在子进程创建新AOF文件的过程中,记录服务器执行的所有写命令。由于子进程进行AOF重写时,主进程仍然需要继续处理命令请求,而这些新的命令可能会对现有的数据库状态进行修改,从而导致服务器当前的数据库状态和重写后的AOF文件中的数据不一致。为了避免这种情况,Redis服务器会将执行完的写命令同时追加到AOF缓冲区和AOF重写缓冲区中。

当子进程完成新AOF文件的创建后,服务器会将重写缓冲区中的所有内容追加到新AOF文件的末尾,以确保新的AOF文件保存的数据库状态与服务器当前的数据库状态一致。最后,服务器会用新的AOF文件替换旧的AOF文件,完成AOF文件重写操作。

四、AOF重写机制的优势

  1. 节省磁盘空间:通过重写机制,可以删除AOF文件中的冗余和无效命令,从而减小文件的大小并节省磁盘空间。

  2. 加快数据恢复速度:因为新的AOF文件更小,所以在进行数据恢复时也会更快。

五、AOF重写机制的注意事项

  1. 性能影响:在AOF重写期间,Redis的性能可能会受到一定的影响,因为子进程需要读取内存中的数据库状态并写入到磁盘上。因此,在配置Redis的自动重写参数时,需要权衡重写频率和性能之间的关系。

  2. 错误处理:如果在重写过程中出现了错误(例如磁盘空间不足),Redis会记录一个错误日志,并继续使用旧的AOF文件进行持久化。在这种情况下,管理员需要检查并修复导致错误的问题,然后再次触发AOF重写。

  3. 数据一致性:在AOF重写过程中,主进程仍然可以接收客户端的写请求。为了确保数据的一致性,Redis会维护一个重写缓冲区来记录重写期间的写操作。当重写完成后,这些写操作会被追加到新的AOF文件中。

面试官:有没有什么办法既能保留RDB持久化的快速加载恢复优点,又保留AOF更少的数据丢失风险优点呢?

Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。以下是Redis混合持久化的详细实现过程:

一、混合持久化的实现步骤

Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录两次快照生成期间的命令操作。

这样一来,快照不用很频繁地执行,这就避免了频繁生成RDB的fork操作对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。

如下图所示,T1 和 T2 时刻的修改,用 AOF 日志记录,等到第二次做全量快照时,就可以清空 AOF 日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。

三、混合持久化的配置与启用

要在Redis中启用混合持久化,需要进行以下配置:

  1. 编辑Redis配置文件

    • 找到Redis配置文件redis.conf,通常在/etc/redis/目录下或用户自定义的目录下。
  2. 配置AOF和RDB

    • 确保appendonly配置项存在并设置为yes,以启用AOF持久化。
  3. 启用混合持久化模式

    • 在redis.conf中找到aof-use-rdb-preamble参数,并设置为yes。
  4. 重新启动Redis服务

    • 执行systemctl restart redis命令或相应的命令来重新启动Redis服务,以使配置生效。

面试官:Redis的主从复制了解吗,为什么Redis需要主从复制,请说说看Redis主从复制的过程?以及Redis主从复制如何保证主从节点的数据一致性?

一、主从复制的作用

Redis主从复制的作用主要体现在以下几个方面:

数据备份和冗余

主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。通过将主节点上的数据复制到从节点上,可以确保数据的安全性和持久性。即使主节点发生故障,从节点仍然可以接管并提供服务,保证数据的可用性和业务连续性。

读写分离

在主从复制架构中,主节点通常负责处理写操作和部分读操作,而从节点则负责处理读操作,这种读写分离的机制可以有效地分担主节点的压力。

故障恢复和高可用性

当主节点发生故障时,哨兵机制可以快速将从节点提升为新的主节点,从而实现故障的快速恢复。这种故障转移机制可以确保系统的高可用性,减少因单点故障导致的业务中断时间。主从复制是哨兵和集群能够实施的基础,为Redis的高可用性提供了有力支持。

二、主从复制流程

  • ### 建立连接与准备阶段
  1. 从节点连接主节点

    • 当一个Redis从节点第一次连接到主节点时,它会发送一个SYNC或PSYNC命令来请求数据同步。
  2. 主节点准备快照

    • 主节点接收到SYNC或PSYNC命令后,会执行BGSAVE命令来生成一个RDB快照文件。这个快照文件包含了主节点当前内存中的所有数据。
    • 在生成快照期间,主节点仍然会继续处理新的写操作。这些写操作会被缓存在内存中,并在快照生成完成后发送给从节点。
  • ### 全量复制阶段
  1. 传输快照文件

    • 主节点将生成的RDB快照文件发送给从节点。从节点接收到快照文件后,会清空当前数据库,然后加载这个快照文件,以确保从节点的数据与主节点一致。
  2. 同步增量数据

    • 在快照文件传输期间,主节点可能会收到新的写操作。这些写操作会被记录在复制缓冲区(Replication Buffer)中。
    • 快照文件传输完成后,主节点会将复制缓冲区中的增量数据发送给从节点。从节点接收到这些增量数据后,会按顺序执行这些操作,以确保数据与主节点一致。

  • ### 增量复制阶段(命令传播)
  1. 持续同步写操作
  2. 全量复制完成后,主节点和从节点之间会建立一个持久的网络连接。主节点会通过这个连接将后续收到的写操作实时同步给从节点。这个过程称为基于长连接的命令传播。从节点接收到这些写操作后,会立即执行这些操作,以保持与主节点的数据一致性。
  • 处理网络中断与重连

  1. 网络中断

    • 如果主从节点之间的网络连接中断,从节点会尝试重新连接主节点。
  2. 使用PSYNC命令进行部分复制

    • 从Redis 2.8版本开始,引入了PSYNC命令来支持部分复制。当从节点重新连接主节点时,它会发送PSYNC命令,并带上自己当前的复制偏移量(Replication Offset)。
    • 主节点会根据从节点的复制偏移量来判断是否需要执行全量复制还是部分复制。
    • 如果从节点的复制偏移量在主节点的复制积压缓冲区(Replication Backlog)范围内,主节点只会发送断开期间的增量数据给从节点,从而避免了全量复制的开销。
  3. 复制积压缓冲区

    • 复制积压缓冲区是一个环形缓冲区,用于存储最近的写操作命令。
    • 它的大小可以通过配置参数 repl-backlog-size 来设置。
    • 当从节点重新连接主节点时,主节点会根据从节点的复制偏移量在复制积压缓冲区中查找对应的命令,并将这些命令发送给从节点。

三、Redis主从复制数据一致性

Redis主从复制在网络延迟或者主节点/从节点出现故障的情况会出现数据不一致性的现象。

为了解决数据不一致的问题,应该尽量保证主从节点之间的网络连接状况良好,比如说避免在不同机房之间部署主从节点,以减少网络延迟。但可能会带来新的问题,就是整个机房都挂掉的情况。

此外,Redis 本身也提供了一些机制来解决数据不一致的问题,比如说通过 Redis 的 INFO replication 命令监控主从节点的复制进度,及时发现和处理复制延迟。

具体做法是获取主节点的 master_repl_offset 和从节点的 slave_repl_offset,计算两者的差值。如果差值超过预设的阈值,采取措施(如停止从节点的数据读取)以减少读到不一致数据的情况。

可以通过如下的参数优化减小主从复制数据不一致的可能性:合理的超时设置

  1. 通过设置 repl-timeout 参数,控制从节点等待主节点数据的最长时间。如果超出这个时间,从节点会尝试重新建立连接并进行同步。

复制积压缓冲区大小调整

  1. 增大 repl-backlog-size 参数的值,以避免因复制积压缓冲区溢出而导致的数据丢失。复制积压缓冲区用于存储主节点尚未发送给从节点的写命令。

原文阅读