Redis持久化机制

118 阅读8分钟

Redis持久化机制

利用永久性存储介质将数据进行保存,在特定的时间将保存的数据恢复到工作状态的工作机制称为持久化

为什么要做持久化 ?为了防止防止服务重启、宕机时内存数据丢失,确保数据的安全性

  • RDB ( Redis DataBase ) 把数据以快照的形式完全备份在硬盘中,关注数据本身

  • AOF( Append Only File ) 把数据保存到硬盘中,关注操作过程

    image-20221111093638084

RDB

是Redis DataBase缩写快照,是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。

image-20221110173054517

RDB触发机制

  • save命令(同步):客户端向Redis发送save命令来创建一个快照文件。执行save命令的时候,如果存在老的快照文件,新的将会替换老的。

    tip: 如果数据量太大,同步数据会执行很久,这期间 Redis 服务器也无法接收其他请求,导致不可用。(不建议使用)

  • bgsave命令(异步):客户端向Redis发送bgsave命令,Redis调用fork创建一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。

    在没有CopyOnWrite之前,我们要给子进程生成虚拟空间,并为虚拟空间地每一个部分分配对应地物理空间,接着要把父进程对应部分地物理空间地内容复制到子进程的空间中。这实际上是个既耗时又耗费空间地操作。

    有了COW之后, fork子进程时,我们只为其生成虚拟空间,但是并不先为每个部分分配真实的物理空间,而是让每个虚拟空间部分仍然指向父进程的物理空间。只有当父进程或子进程修改相应的共享内存空间时,才会为子进程分配物理空间并把父进程的物理空间内容进行复制。这就是所谓的写时复制,即把内存的复制延迟到了内存写入的时刻

    同时需要注意地是,父子进程共享的空间粒度是页( 在Linux中,页的大小为4KB ), 父/子进程修改某个页时,该页的共享才结束,同时子进程分配该页大小的物理空间复制父进程对应页的内容。这样,如果当子进程运行期间,父子进程都没有修改数据,那么操作系统就节省了大量的内存复制时间和占用空间。

    • 通过 fork 创建的子进程能够获得和父进程完全相同的内存空间,父进程对内存的修改对于子进程是不可见的,两者不会相互影响;
    • 通过 fork 创建子进程时不会立刻触发大量内存的拷贝,内存在被修改时会以页为单位进行拷贝,这也就避免了大量拷贝内存而带来的性能问题;
  • 服务器配置定期自动触发

    在 redis.conf 中配置: save 多少秒内数据变了多少 通过配置文件触发持久化的方式与 bgsave 命令类似,达到触发条件时会 fork 一个子进程进行数据保存

    image-20221110204133443

    tip:线上的 Redis 环境不建议使用此方式。因为如果设置触发的时间太短,则容易频繁写入 rdb 文件,影响服务器性能,时间设置太长则会造成数据丢失。

为什么Redis的RDB备份不用多线程实现CopyOnWrite?

进程级的 COW触发时机是在 父进程/子进程对某个物理页的写入,这是由OS控制的;线程级的COW触发时机是在线程对共享数据写入,这里的数据是逻辑上的,需要开发者介入人为控制,比如上锁。这里拿 Java CopyOnWriteList 举例子不太恰当,而如果通过开发者自我实现,将被修改数据之前的副本拷贝到其他内存空间,这部分SNAPSHOT便得以保留,这样一来其实无论线程还是进程,都可以实现dump 出某个时间节点的内存快照的效果,只不过线程实现更麻烦而已

AOF

AOF(Append Only File)持久化,则是会记录客户端对服务器的每一次写操作命令,并将这些写操作以 Redis 协议追加保存到以后缀为 aof 文件末尾,在 Redis 服务器重启时,会加载并运行 aof 文件的命令,以达到恢复数据的目的。当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

image-20221110204402930

Redis 默认不开启 AOF 持久化方式,我们可以在 redis.conf 配置文件中开启并进行更加详细的配置

image-20221111093737728

AOF三种策略对比

image-20221110204447549

image-20221110204530675

AOF重写机制(Redis 中的 AOF 文件太大了怎么办?)

因为 AOF 持久化是通过保存被执行的写命令来记录数据库状态的,随着 AOF 文件内容越来越多,文件的体积也越来越大。如果不对 AOF 文件加以管控的话,可能会对 Redis 服务器产生影响

image-20221110204637214

重写后

image-20221110204646960

Redis 服务器可以创建一个新的 AOF 文件来代替旧的 AOF 文件,重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合 Redis 会fork出一条新进程,读取内存中的数据,并重新写到一个临时文件中。并没有读取旧文件。最后替换旧的aof文件。

Redis 不希望 AOF 重写造成服务器无法处理请求, 所以 Redis 决定将 AOF 重写程序放到后台子进程里执行, 这样处理的最大好处是:

  • 子进程进行 AOF 重写期间,主进程可以继续处理命令请求。

  • 子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。

    注意:由于是模拟快照的过程,因此在重写AOF文件时并没有读取旧的AOF文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件

AOF触发机制

  • 在 redis.conf 中配置

image-20221111093114814

  • 执行 bgrewriteaof 命令

    image-20221111093150421

RDB-AOF混合持久化

redis4.0之后才支持,默认开启

  1. 优点:混合持久化结合了RDB持久化 和 AOF 持久化的优点,采取了rdb的文件小易于灾难恢复,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
  2. 缺点:兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,需要专业的工具来阅读,因为是二进制,所以阅读性较差。

原理

混合持久化也是通过bgrewriteaof完成的,所以基本流程和上述一样。不同的是当开启混合模式时,fork出的子进程先将共享的内存副本全量以RDB的方式写入aof。这样提高了速度也极大的缩小了aof文件(毕竟都是二进制)。写完还是通知主进程,然后再将重写缓冲区的内容以AOF方式写入到文件,然后替换旧的aof文件。也就是说这种模式下的aof文件发生rewrite后前半部分是rdb格式(REDIS开头的二进制数据),后半部分是正常的aof追加的命令(重写缓冲区里的)。

如何选择合适的持久化方式

  1. 如果想达到数据足够的安全性,你应该同时使用两种持久化功能。
  2. 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
  3. 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
  4. 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB。

持久化文件加载流程

当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。

RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢? 建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。

image-20221111094216904

AOF和RDB文件都可以用于服务器重启时的数据恢复。AOF持久化开启且存在AOF文件时,优先加载AOF文件;AOF关闭或者AOF文件不存在时,加载RDB文件;加载AOF/RDB文件城后,Redis启动成功;AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。