> 文章列表 > Redis之持久化讲解

Redis之持久化讲解

Redis之持久化讲解

文章目录

  • 1 Redis持久化
    • 1.1 持久化流程
    • 1.2 RDB机制
      • 1.2.1 save触发方式
      • 1.2.2 bgsave触发方式
      • 1.2.3 自动触发
      • 1.2.4 RDB的优势和劣势
        • 1.2.4.1 优势
        • 1.2.4.2 劣势
    • 1.3 AOF机制
      • 1.3.1 写后日志优势和风险
      • 1.3.2 AOF三种触发机制
      • 1.3.3 文件重写
        • 1.3.3.1 重写的作用
        • 1.3.3.2 重写的过程
        • 1.3.3.3 总结
      • 1.3.4 优缺点
        • 1.3.4.1 优点
        • 1.3.4.2 缺点
    • 1.4 两者混合

1 Redis持久化

Redis数据是存储在内存中的,但是我们都知道内存的数据变化是很快的,也容易发生丢失,为了保证Redis数据不丢失,那就要把数据从内存存储到磁盘上,以便在服务器重启后还能够从磁盘中恢复原有数据,这就是Redis的数据持久化。
Redis数据持久化有三种方式:

  • AOF 日志(Append Only File,文件追加方式):记录所有的操作命令,并以文本的形式追加到文件中。
  • RDB 快照Redis DataBase):将某一个时刻的内存数据,以二进制的方式写入磁盘(早期默认方式)。
  • 混合持久化方式:Redis 4.0 新增了混合持久化的方式,集成了 RDBAOF 的优点

1.1 持久化流程

既然redis的数据可以保存在磁盘上,那么这个流程是什么样的呢?
要有下面五个过程:

  • 客户端向服务端发送写操作(数据在客户端的内存中)
  • 数据库服务端接收到写请求的数据(数据在服务端的内存中)
  • 服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)
  • 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)
  • 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)

1.2 RDB机制

RDB其实就是把数据以快照的形式保存在磁盘上。什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是 默认的持久化方式(早期),这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb
RDB采用的是内存快照的方式,它记录的是某一时刻的数据,而不是操作,所以采用RDB方法做故障恢复时只需要直接把RDB文件读入内存即可,实现快速恢复。

在我们安装了redis之后,所有的配置都是在redis.conf文件中,里面保存了RDBAOF两种持久化机制的各种配置

既然RDB机制是通过把某个时刻的所有数据生成一个快照来保存,那么就应该有一种触发机制,是实现这个过程。对于RDB来说,提供了三种机制:savebgsave自动化。我们分别来看一下

1.2.1 save触发方式

该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下:
在这里插入图片描述
执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取

1.2.2 bgsave触发方式

执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体流程如下:
在这里插入图片描述
具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上Redis内部所有的RDB操作都是采用bgsave命令即默认配置

在执行快照的同时,Redis 会借助操作系统提供的写时复制技术(Copy-On-Write, COW),正常处理写操作。bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件

Redis是怎么解决在bgsave做快照的时候允许数据修改呢?
这里主要是利用bgsave的子线程实现的,具体操作如下:
如果主线程执行读操作,则主线程和 bgsave 子进程互相不影响;
如果主线程执行写操作,则被修改的数据会复制一份副本,然后,主线程在这个数据副本上进行修改。同时,bgsave 子进程可以继续把原来的数据 写入 RDB 文件,在这个过程中,主线程仍然可以直接修改原来的数据
Redis之持久化讲解

虽然 bgsave 执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销:

  • 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环(所以,在 Redis 中如果有一个 bgsave 在运行,就不会再启动第二个 bgsave 子进程)
  • 另一方面,bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork 这个创建过程本身会阻塞主线程 ,而且 主线程的内存越大,阻塞时间越长

需要注意RedisRDB 的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis 的稳定性,所以在 Redis 4.0 后,增加了 AOFRDB 混合的数据持久化机制: 把数据以 RDB 的方式写入文件,再将后续的操作命令以 AOF 的格式存入文件,既保证了 Redis 重启速度,又降低数据丢失风险,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作

1.2.3 自动触发

自动触发是由我们的配置文件来完成的。在redis.conf配置文件中,里面有如下配置,我们可以去设置:

  • save:这里是用来配置触发 RedisRDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如save m n。表示m秒内数据集存在n次修改时,自动触发bgsave
    默认如下配置:
    表示900 秒内如果至少有 1 key的值变化,则保存save 900 1
    表示300 秒内如果至少有 10 key 的值变化,则保存save 300 10
    表示60 秒内如果至少有 10000 key 的值变化,则保存save 60 10000
    不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。
  • stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
  • rdbcompression :默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
  • rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
  • dbfilename :设置快照的文件名,默认是 dump.rdb
  • dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名

1.2.4 RDB的优势和劣势

1.2.4.1 优势

RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
RDB在恢复大数据集时的速度比 AOF 的恢复速度要快。

1.2.4.2 劣势

RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

需要注意,RedisRDB 的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis 的稳定性,所以在 Redis 4.0 后,增加了 AOFRDB 混合的数据持久化机制: 把数据以 RDB 的方式写入文件,再将后续的操作命令以 AOF 的格式存入文件,既保证了 Redis 重启速度,又降低数据丢失风险

1.3 AOF机制

全量备份总是耗时的,有时候我们提供一种更加高效的方式AOF,工作机制很简单,redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录

AOF采用的是写后日志的方式,Redis先执行命令把数据写入内存,然后再记录日志到文件中。AOF日志记录的是操作命令,不是实际的数据,如果采用AOF方法做故障恢复时需要将全量日志都执行一遍
Redis之持久化讲解
AOF 里记录的是 Redis 收到的每一条命令,这些命令是以文本形式保存的。
我们以 Redis 收到set testkey testvalue命令后记录的日志为例,看看 AOF 日志的内容。其中,*3 表示当前命令有三个部分,每部分都是由 $+数字 开头,后面紧跟着具体的命令、键或值。这里,数字表示这部分中的命令、键或值一共有多少字节。例如,$3 set 表示这部分有 3 个字节,也就是set命令。
Redis之持久化讲解

1.3.1 写后日志优势和风险

AOF采用的是写后日志的方式,我们平时用的MySQL则采用的是 写前日志,那 Redis为什么要先执行命令,再把数据写入日志呢?
这个主要是由于Redis在写入日志之前,不对命令进行语法检查,所以只记录执行成功的命令,避免出现记录错误命令的情况,而且在命令执行后再写日志不会阻塞当前的写操作

后写日志的风险:

  • 数据可能会丢失:如果 Redis 刚执行完命令,此时发生故障宕机,会导致这条命令存在丢失的风险
    • 如果此时 Redis 是用作缓存,还可以从后端数据库重新读入数据进行恢复。
    • 如果 Redis 是直接用作数据库的话,此时,因为命令没有记入日志,所以就无法用日志进行恢复了。
  • 可能阻塞其他操作:AOF 日志其实也是在主线程中执行,所以当 Redis 把日志文件写入磁盘的时候,还是会阻塞后续的操作无法执行,(即:AOF 虽然避免了对当前命令的阻塞,但可能会给下一个操作带来阻塞风险)
    AOF 日志也是在主线程中执行(写回策略为 always 时),如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。

1.3.2 AOF三种触发机制

  • 每修改同步always:每个写命令执行完,立马同步地将日志写回磁盘,同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
  • 每秒同步everysec:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘
    异步操作,每秒记录,如果一秒内宕机,有数据丢失,
  • 不同步no:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
配置项 写回时机 优点 缺点
Always 同步写回 可靠性高,数据基本不丢失 每个写命令都有落盘,性能影响较大
Everysec 每秒写回 性能适中 宕机时丢失1秒内的数据
No 操作系统控制的写回 性能好 宕机时丢失数据较多

我们就可以根据系统对高性能和高可靠性的要求,来选择使用哪种写回策略了。

  • 想要获得高性能,就选择 No 策略;
  • 想要得到高可靠性保证,就选择 Always 策略;
  • 允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择 Everysec 策略

1.3.3 文件重写

1.3.3.1 重写的作用

AOF 是以文件的形式在记录接收到的所有写命令。随着接收的写命令越来越多,AOF 文件会越来越大。这也就意味着,我们一定要小心 AOF 文件过大带来的性能问题,主要在于以下三个方面:

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

AOF 重写机制就是在重写时,Redis 根据数据库的现状创建一个新的 AOF 文件,也就是说,读取数据库中的所有键值对,然后对每一个键值对用一条命令记录它的写入 。重写机制具有多变一功能。所谓的多变一,也就是说,旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。
Redis之持久化讲解

1.3.3.2 重写的过程

AOF 日志由主线程写回不同,重写过程是由 后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程 ,导致数据库性能下降。
可以把重写的过程总结为 一个拷贝,两处日志

  • 一个拷贝:就是指,每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程。此时,fork 会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。
  • 第一处日志:指的是因为主线程未阻塞,仍然可以处理新来的操作,Redis 会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。
  • 第二处日志:就是指新的 AOF 重写日志。这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的 AOF 文件,以保证数据库最新状态的记录。

此时,我们就可以用新的 AOF 文件替代旧文件了。
Redis之持久化讲解

为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork出一条新进程来将文件重写。
在这里插入图片描述

1.3.3.3 总结

总结来说,每次 AOF 重写时,Redis 会先执行一个内存拷贝,用于重写;然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为 Redis 采用子进程进行日志重写,所以,这个过程并不会阻塞主线程 。

正因为记录的是操作命令,而不是实际的数据,所以,用 AOF 方法进行故障恢复的时候,需要逐一把操作日志都执行一遍。如果操作日志非常多,Redis 就会恢复得很缓慢,影响到正常使用

1.3.4 优缺点

1.3.4.1 优点

  • AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
  • AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
  • AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
  • AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据

1.3.4.2 缺点

  • 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
  • AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的
  • 以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来

1.4 两者混合

虽然 bgsave 执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销。

  • 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环(所以,在 Redis 中如果有一个 bgsave 在运行,就不会再启动第二个 bgsave 子进程)
  • 另一方面,bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork 这个创建过程本身会阻塞主线程,而且 主线程的内存越大,阻塞时间越长

RDBAOF到底该如何选择
选择的话,两者加一起才更好。因为两个持久化机制你明白了,剩下的就是看自己的需求了,需求不同选择的也不一定,但是通常都是结合使用。有一张图可供总结:
在这里插入图片描述

需要注意RedisRDB 的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis 的稳定性,所以在 Redis 4.0 后,增加了 AOFRDB 混合的数据持久化机制,简单来说:内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作,这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销
Redis之持久化讲解