redis 3 - 持久化

一、概述

(本质是Redis是个有状态服务,需要考虑状态不丢失)

Redis是内存数据库。一旦断电,内存中的数据都会丢失。因此Redis需要对数据进行持久化,防止数据丢失。

Redis有两种持久化技术,分别是AOF和RDB。

二、AOF

Append Only File. 即把所有的写操作以追加的方式记录在文件里。

Redis在重启的时候,通过回放文件中的操作,就可以恢复数据。

在Redis中AOF功能默认是关闭的,可以通过修改redis.conf配置文件中的参数开启。

1
2
appendonly			yes								//是否开启,默认no
appendfilename "appendonly.aof" //持久化文件名称

AOF日志写入

Redis先执行写命令,再将命令记录到AOF日志中,有两个好处:

  1. 避免额外的检查开销。
    • 先执行写操作再记录日志,只有当命令执行成功时,才将命令记录下来。这样可以保证记录在AOF日志里的命令都是可执行的、正确的。
  2. 不会阻塞当前写操作的执行
    • 因为先执行写操作。

也有两个风险:

  1. 如果当Redis没有把命令写入到磁盘时,服务器宕机了,数据就会有丢失的风险
  2. 虽然不阻塞当前写操作,但是可能阻塞下一个写操作。
    • 如果写入硬盘时,硬盘的IO压力大,就会导致写入的速度比较慢,阻塞后面的命令执行。

这两个风险都和「AOF日志写回硬盘的时机」相关。

三种写回策略

Redis写入AOP日志的过程

Redis写入AOP日志的过程,如图所示,具体分为三步:

  1. Redis执行完成命令后,将该命令写到应用程序缓冲区
  2. 执行write命令,将该命令从应用程序缓冲区写入内核缓冲区
  3. 执行fsync命令,将该命令从内核缓冲区写入硬盘

「AOF日志写回硬盘的时机」其实就是指的第三步的时机。

redis.conf配置文件中,appendfsync配置项有三种可选参数

  • Always
    • 每次写命令执行完成后,同步执行fysnc函数
  • Everysec
    • 一秒后创建一个异步任务执行fysnc函数
  • No
    • 永不执行fsync函数,由操作系统控制写回时机。

这三种策略其实是可靠性和性能的权衡。

写回策略 写回时机 数据可靠性 性能
Always 同步写回
Everysec 每秒异步写回 中,宕机会丢失一秒内数据 适中
No 操作系统控制写回 低,宕机会丢很多数据

AOF重写机制

AOF日志是一个文件,随着写操作命令越来越多,文件会变得越来越大。

会造成一个问题:Redis重启后,回放命令的过程比较长,数据恢复时间久。

Redis为了避免AOF文件越来越大,提供了AOF重写机制
当AOF文件大小超过设定的阈值,就会启动「AOF重写」,压缩AOF文件。

「AOF重写」的执行逻辑是:
读取当前数据库所有键值对,将每个键值对转换成一条命令记录到新的AOF文件,记录完成后,用新的AOF文件替换现有的AOF文件。

优点在于:只需要记录结果,相当于把对同一个数据键的多次操作合并了。

AOF后台重写

AOF重写时,需要读取数据库里所有键值对,将每个键值对都转换成命令记录下来。

这个过程非常耗时,所以不能放在主进程里。

所以,Redis重写AOF过程是由后台子进程bgrewriteaof来完成的。

这样的话,子进程进行AOF重写期间,主进程可以继续处理命令,避免阻塞主进程。

具体操作是:

  1. 主进程fork调用系统生成子进程,操作系统把主线程的「页表」复制一份给子进程。
    • 这个页表记录虚拟地址到物理地址的映射关系,而不会复制物理内存。
    • 在不修改内存数据的情况下,子进程和父进程是共享了物理内存数据的。
  2. 父进程修改内存数据时,会系统中断进入「写时复制」流程
    • 发生写操作的时候,复制物理内存,将父子进程对应的物理内存分离。
    • 只会复制修改的物理内存,没修改的还是和子进程共享。
  3. 子进程用复制的一份数据重写完成AOF文件后,会给父进程发送一个信号,父进程收到信号后,调用信号处理函数。完成AOF文件的替换。

在这个流程中有三个阶段会阻塞父进程

  1. 创建子进程时,需要复制父进程的页表。阻塞时间和页表大小有关
  2. 创建子进程后,如果父进程修改了共享的数据,会发生「写时复制」,复制物理内存。内存越大阻塞时间越长。
  3. 信号处理函数执行时。

上述描述只关注了「存量数据」,即页表复制完成那一时刻的数据。

对于增量数据,Redis提供了一个重写缓冲区,在信号处理函数中将重写缓冲区里的内容追加到新的AOF文件中。

所以在AOF重写的过程中,对于写操作,Redis会将它写入两个缓冲区。

三、RDB

RDB也是将数据持久化到日志文件中,但是和AOF不同,它记录的是二进制数据。是某一时刻的全量数据快照。

在Redis恢复数据时,RDB的效率更高一些,因为直接将RDB文件读入内存即可,不需要像AOF那样回放操作。

RDB的快照是全量快照,所以执行快照是一个比较重的操作,如果频率太频繁,可能会对Redis性能产生影响。

这就决定了它执行地不能太频繁,而这样,在宕机时就会丢失更多数据。

Redis提供了两个命令来生成RDB文件,分别是savebgsave。区别在于是否在主线程执行。

可以通过配置文件选项来实现每隔一段时间自动执行一次bgsave命令,默认提供以下配置

1
2
3
save 900 1
save 300 10
save 60 10000

只要满足任意一个条件,就会执行一次bgsave命令。它们的意思分别是:

  • 900秒内,对数据库进行了至少1次修改
  • 300秒内,对数据库进行了至少10次修改
  • 60秒内,对数据库进行了至少10000次修改

RDB与AOF结合

这里发现,AOF重写时其实也是记录下当时全量数据的快照。

所以用RDB快照的方式代替AOF重写,就能获得这两种持久化方式的优点。

Redis4.0提供了这种混合使用的方法。

redis.conf中将配置项设置为yes

1
aof-use-rdb-preamble yes

四、总结

Redis是一种内存数据库,而内存在断电时就会丢失数据,因此需要对数据进行持久化。

Redis支持两种持久化方式,分别是

  1. AOF,记录下所有写操作,在重启时回放写操作来恢复数据。
  2. RDB,备份数据的二进制文件,在重启时直接读取文件来恢复数据。

AOF是以记录增量操作的方式记录数据,所以记录频次相对较高,优点是丢失数据比较少。

但是这样就会记录许多无用操作(对一个键的多次操作,我们其实只关心结果),这样文件的大小就会很快地膨胀,很影响数据恢复的效率;

Redis提供了AOF重写机制,在AOF文件大小超过阈值时,将它进行重写,来压缩文件大小。(重写逻辑是将当前所有键值对转换成AOF文件格式)

RDB是以全量快照的方式记录数据,记录频次比较低,宕机时会丢失更多数据。

但是重启时RDB格式恢复比较快,因为它记录的是二进制数据,并且没有重复数据。

这里发现,AOF重写其实逻辑和RDB是一样的(都是记录当前快照)

Redis高版本提供了两种技术结合使用的方式,AOF负责记录增量数据,AOF重写使用RDB替代。

这样既能在对性能影响比较小的情况下丢失比较少的数据(AOF的优点),也可以拥有比较快的数据恢复速度(RDB的优点)。


redis 3 - 持久化
https://yzaf.top/2023/redis/redis-3/
作者
why
发布于
2023年4月30日
许可协议