淘先锋技术网

首页 1 2 3 4 5 6 7

目录

RDB:snapshotting 快照

RDB 创建快照时会阻塞主线程吗?

AOF:只追加文件

AOF 日志是如何实现的?

为什么是在执行完命令之后记录日志呢?

AOF 重写了解吗?

为什么AOF重写不采用覆盖的方式

RDB与AOF的比较

RDB 比 AOF 优秀的地方 :

AOF 比 RDB 优秀的地方 :

Redis 4.0 对于持久化机制做了什么优化?


RDB:snapshotting 快照

        Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本,而后采用特定的二进制格式进行压缩,使其能够占用更少的空间。我们进行恢复时,直接加载RDB文件,就可以恢复数据库状态,速度很快。

        因为我们是每隔一段时间才会生成一个快照,所以发生服务器宕机时,丢失的数据要比AOF多,同时,由于快照是保存全量数据,所以不能频繁的执行,否则会影响Redis性能。而 AOF 日志可以以秒级的方式记录操作命令,所以丢失的数据就相对更少。

  1. Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能)
  2. 可以将快照留在启动目录中,redis启动时会自动加载。

        快照持久化是 Redis 默认采用的持久化方式,在 redis.conf 配置文件中默认有此下配置:

save 900 1           #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发bgsave命令创建快照。

save 300 10          #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发bgsave命令创建快照。

save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发bgsave命令创建快照。

RDB 创建快照时会阻塞主线程吗?

Redis 提供了两个命令来生成 RDB 快照文件:

  • save : 主线程执行,会阻塞主线程;
  • bgsave : fork一个子线程执行持久化操作,不会阻塞主线程,默认选项。

AOF:只追加文件

        与快照持久化相比,AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:

appendonly yes    

        开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到内存缓存 server.aof_buf 中,然后再根据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。

        AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof

appendfsync always    #每次有数据修改发生时都会写入磁盘中的AOF文件,这样会严重降低Redis的速度
appendfsync everysec  #每秒钟同步一次,执行完命令之后先将命令写入到aof_buf之中,而后每一秒钟进行一次持久化操作
appendfsync no        #让操作系统决定何时进行同步,但是操作系统写回硬盘的时机是不可预知的,如果 AOF 日志内容没有写回硬盘,一旦服务器宕机,就会丢失不定数量的数据。

        为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

         从源码角度看三种不同的写回策略,本质上就是看执行fsync()和write()的执行

        如果想要应用程序向文件写入数据后,能立马将数据同步到硬盘,就可以调用 fsync() 函数,这样内核就会将内核缓冲区的数据直接写入到硬盘,等到硬盘写操作完成后,该函数才会返回。

  • Always 策略就是每次写入 AOF 文件数据后,就执行 fsync() 函数;
  • Everysec 策略就会创建一个异步任务来执行 fsync() 函数;
  • No 策略就是永不执行 fsync() 函数;

 

AOF 日志是如何实现的?

        关系型数据库(如 MySQL)通常都是执行命令之前记录日志(方便故障恢复),而 Redis AOF 持久化机制是在执行完命令之后再记录日志。

为什么是在执行完命令之后记录日志呢?

  • 避免额外的检查开销,AOF 记录日志不会对命令进行语法检查;
  • 在命令执行完之后再记录,不会阻塞当前的命令执行。

这样也带来了风险(我在前面介绍 AOF 持久化的时候也提到过):

  • 如果刚执行完命令 Redis 就宕机会导致对应的修改丢失;
    • 因为如果先将写操作命令记录到 AOF 日志里,再执行该命令的话,如果当前的命令语法有问题,那么如果不进行命令语法检查,该错误的命令记录到 AOF 日志里后,Redis 在使用日志恢复数据时,就可能会出错。

      而如果先执行写操作命令再记录日志的话,只有在该命令执行成功后,才将命令记录到 AOF 日志里,这样就不用额外的检查开销,保证记录在 AOF 日志里的命令都是可执行并且正确的。

  • 可能会阻塞后续其他命令的执行(AOF 记录日志是在 Redis 主线程中进行的),因为当写操作命令执行成功后,才会将命令记录到 AOF 日志。

        当然,AOF 持久化功能也不是没有潜在风险。

        第一个风险,执行写操作命令和记录日志是两个过程,那当 Redis 在还没来得及将命令写入到硬盘时,服务器发生宕机了,这个数据就会有丢失的风险

        第二个风险,前面说道,由于写操作命令执行成功后才记录到 AOF 日志,所以不会阻塞当前写操作命令的执行,但是可能会给「下一个」命令带来阻塞风险

        因为将命令写入到日志的这个操作也是在主进程完成的(执行命令也是在主进程),也就是说这两个操作是同步的。

 

AOF 重写了解吗?

        AOF 持久化是通过保存对数据库中的数据进行修改的指令来实现的,所以AOF文件随着时间的流逝一定会变得越来越大。

        当 AOF 变得太大时,Redis 能够在后台自动重写 AOF 产生一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小,原因是但是新的AOF文件不会包含任何浪费空间的冗余命令,redis会使用新的Aof文件替换掉旧的aof文件。

功能的实现:

        AOF 重写是通过读取数据库中的键值对来实现的,实现用最精简的指令来记录数据库中的数据状态,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。

重写流程:

         因为AOF采用单线程机制,我们为了避免在重写过程中阻塞主线程,我们fork一个子进程来实现重写,fork之后新的子进程保存有与主线程相同的数据库状态,但是无法避免的,我们在进行aof指令时,客服端会对数据库进行写操作,如果我们不对写操作进行处理的话,则很有可能产生数据不一致的情况。

        AOF重写不会阻塞主线程,其重写过程是由后台子进程 bgrewriteaof 来完成的,从而避免了性能下降。

BGREWRITEAOF:

  1. 把主线程的内存拷贝一份给fork出来的 bgrewriteaof 子进程,这里面包含了Redis中最新的数据。
  2. 子进程将其中的数据进行重写。
  3. 主线程在重写时维护一个AOF重写缓冲区,将重写过程中的写操作记入其中,当子进程完成创建新 AOF 文件的工作之后,会向主线程发送一个信号,然后服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。
  4. 服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。

为什么AOF重写不采用覆盖的方式

  1. 父子进程写同一个文件必然会产生竞争,如果控制竞争就意味着会影响父进程的性能。
  2. 如果AOF重写失败,那么原本的AOF文件相当于被污染了,就直接废了。而采用覆盖的方式则不会有这种负面影响(重写失败就直接删了,一点影响没有)。

RDB与AOF的比较

RDB 比 AOF 优秀的地方 :

  • RDB 文件存储的内容是经过压缩的二进制数据, 保存着某个时间点的数据集,文件很小,适合做数据的备份,灾难恢复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会必 RDB 文件大很多。当 AOF 变得太大时,Redis 能够在后台自动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。不过, Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。
  • 使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要依次执行每个写命令,速度非常慢。也就是说,与 AOF 相比,恢复大数据集的时候,RDB 速度更快。

AOF 比 RDB 优秀的地方 :

  • RDB 的数据安全性不如 AOF,没有办法实时或者秒级持久化数据。生成 RDB 文件的过程是比繁重的, 虽然 BGSAVE 子进程写入 RDB 文件的工作不会阻塞主线程,但会对机器的 CPU 资源和内存资源产生影响,严重的情况下甚至会直接把 Redis 服务干宕机。AOF 支持秒级数据丢失(取决 fsync 策略,如果是 everysec,最多丢失 1 秒的数据),仅仅是追加命令到 AOF 文件,操作轻量。
  • RDB 文件是以特定的二进制格式保存的,并且在 Redis 版本演进中有多个版本的 RDB,所以存在老版本的 Redis 服务不兼容新版本的 RDB 格式的问题。
  • AOF 以一种易于理解和解析的格式包含所有操作的日志。你可以轻松地导出 AOF 文件进行分析,你也可以直接操作 AOF 文件来解决一些问题。比如,如果执行FLUSHALL命令意外地刷新了所有内容后,只要 AOF 文件没有被重写,删除最新命令并重启即可恢复之前的状态。

Redis 4.0 对于持久化机制做了什么优化?

尽管 RDB 比 AOF 的数据恢复速度快,但是快照的频率不好把握:

  • 如果频率太低,两次快照间一旦服务器发生宕机,就可能会比较多的数据丢失;
  • 如果频率太高,频繁写入磁盘和创建子进程会带来额外的性能开销。
  1.  由于 RDB 和 AOF 各有优势,于是,Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。
  2. 当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

图片

 

        这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快

        加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失