聊聊 Redis 高可用之持久化AOF和RDB分析

2021-04-10  本文已影响0人  瞎胡扯1

Redis 持久化概述

Redis 是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将 Redis 中的数据以某种形式把内存中的数据保存到磁盘中;当 Redis 重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。

Redis 提供了两种不同的持久化方法来讲数据存储到硬盘上 :

下面依次介绍 RDB 持久化和 AOF 持久化。

RDB 持久化

RDB ( Redis Database ) 持久化就是把存储在内存里的数据在某个时间点上写入到硬盘上。在创建快照之后,用户可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,也可以当Redis 重新启动时,读取快照文件恢复数据。

1 创建快照的方式

2 原理分析

上文我们提到了创建 RDB 快照的几种方式,总结可以发现这几种方式无非就是通过 SAVEBGSAVE 命令来生成快照。

由于 SAVE 命令会造成长时间阻塞,而 BGSAVE 命令阻塞时间较短,所以,一般很少使用SAVE 命令,而是用BGSAVE 命令,接下来我们进一步讲解一下 BGSAVE 命令。

虽然 SAVE 命令一直阻塞 Redis 直到快照生成完毕, 但是因为它不需要创建子进程, 并且没有子进程争抢资源,所以 SAVE 创建快照的速度比 BGSAVE 创建快照的速度快些。

fork系统调

fork 系统调用会产生一个子进程,它与父进程共享相同的内存地址空间,这样子进程在这一时刻就能拥有与父进程的相同的内存数据。

虽然子进程与父进程共享同一块内存地址空间,但是在 fork 子进程时,操作系统需要拷贝父进程的内存页表给子进程,如果整个 Redis 实例内存占用很大,那么它的内存页也会很大,在拷贝是就会比较耗时,同时这个过程会消耗大量的 CPU 资源。在完成拷贝之前父进程也处于阻塞状态,无法处理客户端请求。

fork 执行完之后,子进程就可以扫描自身所有的内存数据,然后把全部数据写入到 RDB 文件中。

fork 操作的流程如下所示:

在这里插入图片描述

Copy On Write

前面介绍了 BGSAVE 命令不会造成阻塞主进程接收其他命令的请求,有 fork 系统调用可知,子进程共享主进程的内存数据,那么,当 Redis 服务器接收到写命令时,修改内存数据时,子进程读取同一内存地址的数据时,就会出现脏数据。

如果为了快照而暂停写操作,肯定是不能接受的。所以,Redis 借助了操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

当父进程接收到写操作时,如果要修改的数据在内存中已经存在,那么,主进程就会拷贝一份数据,重新分配新的内存地址空间,这样,父进程就在新申请的内存空间中修改数据,不在与子进程共享,这个过程就是 Copy On Write(写实复制)。

比如我们修改上图的 物理页13 的数据,Copy On Write 后的图如下所:

在这里插入图片描述

这样父子进程的内存就会逐渐分离,父进程申请新的内存空间并更改内存数据,子进程的内存数据不受影响。

由此可以看出,在生成 RDB 文件时,不仅消耗 CPU 资源,还有需要占用最多一倍的内存空间。所以,我们应该保证 Redis 机器拥有 足够的CPU和内存资源,并合理设置生成 RDB 的时机。

3 RDB 的优缺点

RDB 的优点:

RDB 的缺点:

AOF 持久化

AOF 全称成为 Append Only File ( 只追加文件)。简单来说,AOF 持久化会把执行的写命令写到 AOF 文件的末尾,以此来记录数据发生的变化。因此,Redis 只要从头到尾执行一次 AOF 文件中的文件,就可以恢复数据。

1 开启 AOF

Redis 中 AOF 默认是关闭的,在 redis.conf 配置文件中添加如下配置开启。

# 开启AOF
appendonly yes

# AOF文件名
appendfilename "appendonly.aof"

# 文件刷盘方式
appendfsync everysec

2 文件同步

开启 AOF 后,所有的写入命令都会追加到 AOF缓冲区 中, Redis 会根据刷盘策略把 AOF 缓冲区中的数据保存到磁盘中,为了保证数据文件的安全性,Redis 提供了如下刷盘策略:

3 重写/压缩 AOF 文件

随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入了 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新的 AOF文件的过程。

文件变小原因

  1. 进程内一超时的数据不再写入文件。
  2. 旧文件含有的无效命令,重写使用内存数据直接生成,这样新文件中只保留最终数据的写入命令。
  3. 多条写命令合并为一个。

触发方式

AOF 重写可以手动触发和自动触发:

重写机制

BGREWRITEAOF 命令 与 BGSAVE 命令相似,在执行 BGREWRITEAOF 命令后,父进程 调用 fork 创建一个子进程执行 AOF 的重写操作。流程如下所示:

在这里插入图片描述

流程说明:
1、执行 AOF 重写请求

2、父进程执行 fork 创建子进程

3.1、fork 完成后,主进程继续响应其他命令,所有修改命令写入 AOF 缓冲区

3.2、接收的写命令同时也写入 AOF 重写缓冲区中。

4、子进程根据内存快照,按照命令合并规则写入到新的 AOF 文件。

5.1、新 AOF文件写完成后,子进程发送信号给父进程。

5.2、父进程把 AOF 重写缓冲区的数据写入到新的 AOF 文件

5.3、使用新 AOF 文件替换老文件,完成 AOF 重写。

4 AOF 追加阻塞

当开启 AOF 持久化时,常用的同步硬盘策略是 everysec ,用于平衡性能和数据安全性。对于这种方式,Redis 使用另一个线程执行 fsync 同步刷盘。当系统硬盘资源繁忙是,会造成 Redis 主线程阻塞。

阻塞流程

  1. 主线程负责写入 AOF 缓冲区。
  2. AOF 线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间。
  3. 主线程负责对比上次 AOF 同步时间:
    • 如果距上次同步成功时间在 2 秒内,主线程直接返回。
    • 如果距上次同步成功时间超过 2 秒,主线程将会阻塞,直到同步操作完成。

阻塞流程发现问题

  1. everysec 配置最多可能丢失 2 秒数据,而不是 1 秒。
  2. 如果系统 fsync 缓慢,将会导致 Redis 主线程阻塞影响效率。

5 AOF 优缺点

AOF优点:

  1. AOF 可以更好的保护数据不丢失,一般 AOF 会以每隔 1 秒,通过后台的一个线程去执行一次 fsync 操作,如果 Redis 挂掉了,最多丢失 1 秒的数据。
  2. AOF 以 append-only 的模式写入,所以没有任何的磁盘寻址的开销,写入性能非常的高。
  3. AOF 日志文件的命令通过非常可读的方式进行记录,这个非常适合做灾难性的误删除紧急恢复,如果某人不小心用 flushall 命令清空了所有数据,只要这个时候还没有执行 Rewrite,那么就可以将日志文件中的flushall 删除,进行恢复。

AOF缺点:

  1. 对于同一份数据备份文件,AOF 比 RDB大
  2. AOF 开启后支持写的 QPS 会比 RDB 支持的写的 QPS 低,因为 AOF 一般会配置成每秒 fsync 操作,每秒的 fsync 操作还是很高的。
  3. 数据恢复比较慢,不适合做冷备。

参考资料:
《Redis 开发与运维》
《Redis 设计与实现》

上一篇下一篇

猜你喜欢

热点阅读