写点什么

再也不怕面试官问 Redis 持久化了

作者:程序员花卷
  • 2023-12-05
    云南
  • 本文字数:3674 字

    阅读完需:约 12 分钟

再也不怕面试官问Redis持久化了

作者 程序员花卷 | Java 后端研发工程师 | 三四线程序员 | 有追求的打工仔 |

🅰️AOF 持久化

AOF 持久化功能是通过命令追加的方式保存 Redis 服务器所执行的写命令来记录数据库状态的。因为 AOF 文件里面包含了重建数据库状态所需的所有写命令,所以如果服务器崩溃宕机重启之后只需要重新执行一遍 AOF 文件里面保存的写命令就可以还原崩溃宕机之前的数据。


先执行命令再将命令保存到 AOF,这个主要是为了防止记录的命令出现语法错误,Redis 在向 AOF 里面记录日志的时候,并不会先对这些写命令进行语法检查,所以,如果先记录日志再执行写命令,日志中就有可能记录了有语法错误的写命令,导致恢复数据时出错。写后日志这种方式,就是让系统先执行写命令,再记录日志,只有写命令执行成功,才会被记录到日志中,否则系统就会向客户端报错。


AOF 的开启方式:配置 appendonly yes,默认是不开启的


🔥AOF 持久化的具体步骤

  1. 命令追加:当 AOF 持久化功能处于开启状态时,Redis 服务器在执行完一个写命令之后,会以协议的格式将被执行的写命令追加到服务器的 aof_buf 缓冲区的末尾,此时还没有将写命令保存到 AOF 日志里面。

  2. 文件写入:根据 Redis 配置的写回策略 appendfsync 来决定以什么样的频率将 aof_buf 缓冲区的内容写入到 AOF 日志里面。 三种策略包括 always 、everysec 、no ,默认是 everysec 。


🔥三种写回策略

  1. always 同步写回,即 Redis 执行完写命令后就立即把日志写入到磁盘中,这种方式的优点是可以减少由于 Redis 宕机带来的数据丢失风险,可靠性非常高;缺点是需要立即执行日志落盘的操作,性能比较低。


  1. everysec 每秒写回,即 Redis 执行完写命令后先以协议的格式将这个命令追加到 Redis 服务器的 aof_buf 缓冲区,间隔一秒后再将缓冲区的内容写入到 AOF 日志里面。这种写回策略的优点是性能不错,缺点是由于 Redis 宕机造成的数据丢失风险大大增加,也就是可靠性不是很高。


  1. no 操作系统控制的写回,即 Redis 执行完写命令后,先以协议的格式将这个命令追加到 Redis 服务器的 aof_buf 缓冲区,由操作系统来决定何时将将缓冲区内容写入到 AOF 文件中。这种写回策略的优点是性能非常好,缺点是由于 Redis 宕机造成的数据丢失风险非常大,可靠性不高。


写回策略使用 redis.conf 的 appendfsync 选项来配置


🔥AOF 的优点

  1. 可以避免记录错误的写命令

  2. 可以有效的避免额外的开销,比如语法检查带来的开销

  3. 由于在写命令执行后才写日志,所以不会阻塞当前的写操作


🔥AOF 的缺点

  1. 存在数据和命令丢失的风险,假设 Redis 在执行完某一条命令后,还没来得及写日志,Redis 就宕机了,此时数据和命令都会丢失,如果 Redis 是用作缓存,那还可以从数据库恢复数据,但如果 Redis 直接用作数据库,那这些数据就真的丢失了。

  2. 存在阻塞下一个写操作的风险,虽然 Redis 的 AOF 不会阻塞当前的写操作,但是可能会阻塞下一个写操作,因为记录 AOF 日志这个操作是由主线程执行的,如果再把日志写入磁盘时,磁盘写压力比较大,就会导致写盘比较慢,进而后续操作也无法执行了。

  3. 会存在 AOF 文件过大影响性能以及恢复数据耗时长的问题


🔥AOF 为什么要重写

因为 AOF 是以文件的形式记录接收到的所有写命令的,可能会导致 AOF 文件过大,文件过大不仅会带来性能上的影响,恢复数据时耗费的时间也会很长。为了避免这些影响,Redis 提供了 AOF 文件重写功能,这个功能能生成一个全新的 AOF 文件,并且文件中只包含恢复当前数据库所需要的命令。


🔥AOF 文件重写过程

Redis 每次执行重写,都会 fork 出一个子线程去负责这个事情,并且在 fork 过程中还会将主线程的内存拷贝一份给到子线程,这个拷贝不是一次性拷贝,利用的是操作系统的写时复制(Copy On Write),当新的 AOF 文件生成完毕,也就是重写完毕,子线程就会退出并通知主线程,然后主线程就会使用新的 AOF 文件代替已有的 AOF 文件,借此完成整个重写操作。而且重写是安全的。


🔥AOF 文件重写触发方式

  1. 手动执行 bgrewriteaof 命令

  2. 配置自动触发,有下面两个配置项:::info

  3. auto-aof-rewrite-min-size:设置 AOF 文件重写的最小体积标准,当 AOF 文件的体积小于给定值时,不会触发重写,默认是 64MB

  4. auto-aof-rewrite-percentage:控制触发自动 AOF 重写所需的文件体积增大比例,默认值是 100,表示如果当前 AOF 文件的体积比最后一次 AOF 文件重写后的体积增大了一倍,那么将自动执行重写。:::


🔥AOF 文件重写会阻塞吗?

AOF 的重写过程是主线程 fork 了一个子线程去完成的,所以这个重写过程并不会阻塞主线程。主线程仍然会处理新的请求;但是重写完成后子线程需要通知主线程来完成替换 AOF 文件的工作,主线程在替换过程中会发生阻塞;另一个就是 fork 子线程的过程,要将主线程的内存拷贝一份给子线程,这个过程有阻塞风险。


🅰️RDB 持久化

RDB 持久化指的是将内存中某一时刻的数据保存到 RDB 文件中,避免意外情况导致的数据丢失。


RDB 保存的是数据,而 AOF 保存的是写命令。RDB 文件是一个经过压缩的二进制文件。


🔥RDB 对什么数据进行快照

RDB 为了保证数据的可靠性,所以执行的是全量快照,也就是对所有数据都进行快照保存。

但是这种全量快照的方式非常消耗性能,在进行快照时,主线程会 fork 一个子线程进行快照操作,这个 fork 的过程会阻塞主线程,并且主线程中内存的数据越大,阻塞主线程的时间就越长。所以对于后续的快照采用的是“增量快照”,即做了一次全量快照后,后续的快照只针对修改的数据进行快照记录,可以避免每次全量快照的开销。


🔥RDB 会阻塞主线程吗?

RDB 有两种模式保存快照数据

  1. save 这种方式会阻塞主线程,导致主线程无法处理其他请求

  2. bgsave 这种方式主线程会 fork 一个子线程,由子线程来完成数据的保存,不会阻塞主线程,但是 fork 的过程会阻塞主线程


🔥快照时数据能否被修改

如果在进行 RDB 快照时数据不能被修改,那么会对系统业务以及性能都造成一定的影响。

如果能被修改,那么又会破坏快照的完整性。

Redis 采用了操作系统写时复制(Copy On Write)的机制,如果主线程对这些数据是读操作,那么主线程和子线程互相不影响。但是如果主线程要修改一块数据,比如把 A 修改为 B,那么 A 这块数据就会被复制一份,生成 A 副本,主线程在 A 的副本上进行修改,同时,bgsave 子线程可以继续把原来的数据 A 写入 RDB 文件中。相互不影响。

这样就保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。


🔥写时复制技术的原理

传统做法:fork 子线程时,直接将父线程的数据一次性拷贝到子线程


写时复制:内存数据并非在 fork 子线程时复制,而是在尝试写入时复制,这大大提高了 fork 子线程的效率。


写时复制技术仅复制主线程的页表,页表包括主线程的虚拟地址、物理地址、写入权限字段,虽然都存储写入权限字段,但是不具备真正的写入权限,即无法执行写入操作。假如只进行读取操作,那么父线程和子线程双方都能访问共享的物理页面,当其中一方打算更改数据时,就会执行复制操作,需要按照下面的流程解除共享。

  • 没有写入权限,尝试写入时,引发缺页中断

  • CPU 转到内核模式,缺页中断机制开始运行

  • 对于被访问并且尝试修改的数据,缺页中断机制会复制一份数据副本,然后将其分配给要进行写操作的线程,并根据请求更新其中的内容


🔥写时复制技术的优点

  1. 可以提高 fork 子线程的效率,减少主线程的阻塞时长,提高系统的整体性能。

  2. 可以减少分配和复制大量资源时带来的瞬间延时

  3. 可以减少不必要的资源开销,比如在 fork 子线程时,并不是所有的页面都需要复制,仅仅只是复制页表,对于数据在有写入操作时才会针对性的进行复制。


🔥RDB 快照的频率

RDB 快照的频率可以通过配置文件中的 save 选项进行设置,只要满足其中一个条件,Redis 服务器就会执行 bgsave 命令。下面是例子:

  • save 900 1 表示的是如果在 900 秒内有一次修改,就会触发 bgsave

  • save 300 10 表示的是如果在 300 秒内有 10 次修改,就会触发 bgsave

  • save 60 10000 表示的是如果在 60s 内有 10000 次修改,就会触发 bgsave


🔥RDB 文件载入时机

Redis 没有专门用于载入 RDB 文件的命令,只要 Redis 服务在启动时检测到有 RDB 文件的存在,就会自动载入。Redis 服务在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成。:::info 需要注意的是,因为 AOF 文件的更新频率通常比 RDB 文件的更新频率高,所以如果 Redis 服务开了 AOF 持久化功能,那么 Redis 服务会优先使用 AOF 文件来还原数据。否则才会优先使用 RDB 文件来还原数据。:::


🅰️RDB 与 AOF 的区别

  1. 保存的内容不同:AOF 是以追加的形式保存写命令,RDB 保存的是数据

  2. 可靠性不同:AOF 的可靠性优于 RDB,但也要看具体的配置,不能一概而论

  3. 数据恢复速度不同:通常 RDB 的数据恢复速度要比 AOF 快,因为 AOF 存储的是一条条写入命令,恢复数据时需要逐条执行,而 RDB 保存的是数据,恢复起来比较快。


🅰️RDB 与 AOF 结合使用

虽然 RDB 的数据恢复速度比 AOF 要快很多,但是 RDB 快照的频率不太好控制。频率太低,两次快照之间发生宕机,那么丢失的数据就会比较多,如果频率太高,又会产生额外的开销。


Redis4.0 提出了将 AOF 和 RDB 结合使用的方案,也就是 RDB 快照以一定的频率进行快照记录,AOF 负责记录两次快照之间的所有命令操作。这样就不用频繁的执行快照、也不会造成 AOF 文件过大的问题,避免了 fork 线程带来的开销,也降低了重写 AOF 带来的开销。

发布于: 刚刚阅读数: 4
用户头像

决定你是谁的,不是你的天赋,而是你的努力 2019-09-15 加入

Java后端软件研发工程师,擅长业务软件研发和设计

评论

发布
暂无评论
再也不怕面试官问Redis持久化了_缓存_程序员花卷_InfoQ写作社区