作者 程序员花卷 | Java 后端研发工程师 | 三四线程序员 | 有追求的打工仔
🅰️AOF 持久化
AOF 持久化功能是通过命令追加的方式保存 Redis 服务器所执行的写命令来记录数据库状态的。因为 AOF 文件里面包含了重建数据库状态所需的所有写命令,所以如果服务器崩溃宕机重启之后只需要重新执行一遍 AOF 文件里面保存的写命令就可以还原崩溃宕机之前的数据。
先执行命令再将命令保存到 AOF,这个主要是为了防止记录的命令出现语法错误,Redis在向AOF里面记录日志的时候,并不会先对这些写命令进行语法检查,所以,如果先记录日志再执行写命令,日志中就有可能记录了有语法错误的写命令,导致恢复数据时出错。写后日志这种方式,就是让系统先执行写命令,再记录日志,只有写命令执行成功,才会被记录到日志中,否则系统就会向客户端报错。
AOF的开启方式:配置appendonly yes,默认是不开启的
🔥AOF 持久化的具体步骤
- 命令追加:当 AOF 持久化功能处于开启状态时,Redis 服务器在执行完一个写命令之后,会以协议的格式将被执行的写命令追加到服务器的 aof_buf 缓冲区的末尾,此时还没有将写命令保存到 AOF 日志里面。
- 文件写入:根据 Redis 配置的写回策略 appendfsync 来决定以什么样的频率将 aof_buf 缓冲区的内容写入到 AOF 日志里面。 三种策略包括 always 、everysec 、no ,默认是 everysec 。
🔥三种写回策略
- always同步写回,即Redis执行完写命令后就立即把日志写入到磁盘中,这种方式的优点是可以减少由于Redis宕机带来的数据丢失风险,可靠性非常高;缺点是需要立即执行日志落盘的操作,性能比较低。
- everysec 每秒写回,即Redis执行完写命令后先以协议的格式将这个命令追加到 Redis 服务器的 aof_buf 缓冲区,间隔一秒后再将缓冲区的内容写入到 AOF 日志里面。这种写回策略的优点是性能不错,缺点是由于Redis宕机造成的数据丢失风险大大增加,也就是可靠性不是很高。
- no 操作系统控制的写回,即Redis执行完写命令后,先以协议的格式将这个命令追加到 Redis 服务器的 aof_buf 缓冲区,由操作系统来决定何时将将缓冲区内容写入到 AOF 文件中。这种写回策略的优点是性能非常好,缺点是由于Redis宕机造成的数据丢失风险非常大,可靠性不高。
写回策略使用 redis.conf 的 appendfsync 选项来配置
🔥AOF的优点
- 可以避免记录错误的写命令
- 可以有效的避免额外的开销,比如语法检查带来的开销
- 由于在写命令执行后才写日志,所以不会阻塞当前的写操作
🔥AOF 的缺点
- 存在数据和命令丢失的风险,假设Redis在执行完某一条命令后,还没来得及写日志,Redis就宕机了,此时数据和命令都会丢失,如果Redis是用作缓存,那还可以从数据库恢复数据,但如果Redis直接用作数据库,那这些数据就真的丢失了。
- 存在阻塞下一个写操作的风险,虽然Redis的AOF不会阻塞当前的写操作,但是可能会阻塞下一个写操作,因为记录AOF日志这个操作是由主线程执行的,如果再把日志写入磁盘时,磁盘写压力比较大,就会导致写盘比较慢,进而后续操作也无法执行了。
- 会存在 AOF 文件过大影响性能以及恢复数据耗时长的问题
🔥AOF 为什么要重写
因为AOF是以文件的形式记录接收到的所有写命令的,可能会导致AOF文件过大,文件过大不仅会带来性能上的影响,恢复数据时耗费的时间也会很长。为了避免这些影响,Redis提供了 AOF 文件重写功能,这个功能能生成一个全新的AOF文件,并且文件中只包含恢复当前数据库所需要的命令。
🔥AOF文件重写过程
Redis每次执行重写,都会fork出一个子线程去负责这个事情,并且在fork过程中还会将主线程的内存拷贝一份给到子线程,这个拷贝不是一次性拷贝,利用的是操作系统的写时复制(Copy On Write),当新的AOF文件生成完毕,也就是重写完毕,子线程就会退出并通知主线程,然后主线程就会使用新的AOF文件代替已有的AOF文件,借此完成整个重写操作。而且重写是安全的。
🔥AOF文件重写触发方式
- 手动执行
bgrewriteaof
命令 - 配置自动触发,有下面两个配置项
- auto-aof-rewrite-min-size:设置AOF文件重写的最小体积标准,当AOF文件的体积小于给定值时,不会触发重写,默认是64MB
- 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有两种模式保存快照数据
- save 这种方式会阻塞主线程,导致主线程无法处理其他请求
- bgsave 这种方式主线程会fork一个子线程,由子线程来完成数据的保存,不会阻塞主线程,但是fork的过程会阻塞主线程
🔥快照时数据能否被修改
如果在进行RDB快照时数据不能被修改,那么会对系统业务以及性能都造成一定的影响。
如果能被修改,那么又会破坏快照的完整性。
Redis采用了操作系统写时复制(Copy On Write)的机制,如果主线程对这些数据是读操作,那么主线程和子线程互相不影响。但是如果主线程要修改一块数据,比如把A修改为B,那么A这块数据就会被复制一份,生成A副本,主线程在A的副本上进行修改,同时,bgsave子线程可以继续把原来的数据A写入RDB文件中。相互不影响。
这样就保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
🔥写时复制技术的原理
传统做法:fork子线程时,直接将父线程的数据一次性拷贝到子线程
写时复制:内存数据并非在fork子线程时复制,而是在尝试写入时复制,这大大提高了fork子线程的效率。
写时复制技术仅复制主线程的页表,页表包括主线程的虚拟地址、物理地址、写入权限字段,虽然都存储写入权限字段,但是不具备真正的写入权限,即无法执行写入操作。假如只进行读取操作,那么父线程和子线程双方都能访问共享的物理页面,当其中一方打算更改数据时,就会执行复制操作,需要按照下面的流程解除共享。
- 没有写入权限,尝试写入时,引发缺页中断
- CPU转到内核模式,缺页中断机制开始运行
- 对于被访问并且尝试修改的数据,缺页中断机制会复制一份数据副本,然后将其分配给要进行写操作的线程,并根据请求更新其中的内容
🔥写时复制技术的优点
- 可以提高 fork 子线程的效率,减少主线程的阻塞时长,提高系统的整体性能。
- 可以减少分配和复制大量资源时带来的瞬间延时
- 可以减少不必要的资源开销,比如在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 的区别
- 保存的内容不同:AOF 是以追加的形式保存写命令,RDB保存的是数据
- 可靠性不同:AOF的可靠性优于RDB,但也要看具体的配置,不能一概而论
- 数据恢复速度不同:通常RDB的数据恢复速度要比AOF快,因为AOF存储的是一条条写入命令,恢复数据时需要逐条执行,而RDB保存的是数据,恢复起来比较快。
🅰️RDB与AOF结合使用
虽然RDB的数据恢复速度比AOF要快很多,但是RDB快照的频率不太好控制。频率太低,两次快照之间发生宕机,那么丢失的数据就会比较多,如果频率太高,又会产生额外的开销。
Redis4.0提出了将AOF和RDB结合使用的方案,也就是RDB快照以一定的频率进行快照记录,AOF负责记录两次快照之间的所有命令操作。这样就不用频繁的执行快照、也不会造成AOF文件过大的问题,避免了fork线程带来的开销,也降低了重写AOF带来的开销。