Redis 高可用的绝对的利器——持久化(RDB 和 AOF)

1、简介
Redis 的非常快,很大一部分原因是因为 Redis 的数据存储在内存中,既然在内存中,那么当服务器宕机或者断电的时候,数据就会全部丢失了,所以 Redis 提供了两种机制来保证 Redis 数据不会因为故障而全部丢失,这种机制称为 Redis 的持久化机制。Redis 的持久化机制有两种:
RDB(Redis Data Base) 内存快照
AOF(Append Only File) 增量日志
**RDB(Redis DataBase) **指的是在指定的时间间隔内将内存中的数据集快照写入磁盘,RDB 是内存快照(内存数据的二进制序列化形式)的方式持久化,每次都是从 Redis 中生成一个快照进行数据的全量备份。优点:
存储紧凑,节省内存空间
恢复速度非常快
适合全量备份、全量复制的场景,经常用于灾难恢复(对数据的完整性和一致性要求相对较低的场合)
缺点:
容易丢失数据,容易丢失两次快照之间 Redis 服务器中变化的数据。
RDB 通过 fork 子进程对内存快照进行全量备份,是一个重量级操作,频繁执行成本高。
fork 子进程,虽然共享内存,但是如果备份时内存被修改,最大可能膨胀到 2 倍大小。
**AOF(Append Only File)**是把所有对内存进行修改的指令(写操作)以独立日志文件的方式进行记录,重启时通过执行 AOF 文件中的 Redis 命令来恢复数据。AOF 能够解决数据持久化实时性问题,是现在 Redis 持久化机制中主流的持久化方案(后续会谈到 4.0 以后的混合持久化)。优点:
数据的备份更加完整,丢失数据的概率更低,适合对数据完整性要求高的场景
日志文件可读,AOF 可操作性更强,可通过操作日志文件进行修复
缺点:
AOF 日志记录在长期运行中逐渐庞大,恢复起来非常耗时,需要定期对 AOF 日志进行瘦身处理(后续详述)
恢复备份速度比较慢
同步写操作频繁会带来性能压力
官网地址
2、RDB
2.1 简介
RDB 持久化方案进行备份时,Redis 会单独 fork 一个子进程来进行持久化,会将数据写入一个临时文件中,持久化完成后替换旧的 RDB 文件。在整个持久化过程中,主进程(为客户端提供服务的进程)不参与 IO 操作,这样能确保 Redis 服务的高性能,RDB 持久化机制适合对数据完整性要求不高但追求高效恢复的使用场景。下面展示 RDB 持久化流程:

2.2 Fork
上面说到了 RDB 持久化过程中,主进程会 fork 一个子进程来负责 RDB 的备份,这里简单介绍一下 fork
Linux 操作系统中的程序,fork 会产生一个和父进程完全相同的子进程。子进程与父进程所有的数据均一致,但是子进程是一个全新的进程,与原进程是父子进程关系
出于效率考虑,Linux 操作系统中使用 COW(Copy On Write)写时复制机制,fork 子进程一般情况下与父进程共同使用一段物理内存,只有在进程空间中的内存发生修改时,内存空间才会复制一份出来。
在 Redis 中,RDB 持久化就是充分的利用了这项技术,Redis 在持久化时调用 glibc 函数 fork 一个子进程,全权负责持久化工作,这样父进程仍然能继续给客户端提供服务。fork 的子进程初始时与父进程(Redis 的主进程)共享同一块内存;当持久化过程中,客户端的请求对内存中的数据进行修改,此时就会通过 COW 机制对数据段页面进行分离,也就是复制一块内存出来给主进程去修改。

RDB 触发的规则分为两大类,分别是手动触发和自动触发:自动触发:
配置触发规则
shutdown 触发
flushall 触发
手动触发:
save
bgsave
2.3 自动触发
以下介绍 Redis 的 RDB 持久化机制中的自动触发机制中的配置触发规则来触发 RDB,涉及到 RDB 规则的配置、文件存储路径配置、文件名配置、文件压缩配置、文件完整性校验配置。
2.3.1 配置规则触发
在 Redis 安装目录下的 redis.conf 配置文件中搜索 /snapshot 即可快速定位,配置文件默认注释了下面三行数据,通过配置规则来触发 RDB 的持久化,需要开启或者根据自己的需求按照规则来配置。

下面对配置规则进行解释,实际使用过程中可以根据需求进行合理的配置
save 3600 1 -> 3600 秒内有 1 个 key 被修改,触发 RDBsave 300 100 -> 300 秒内有 100 个 key 被修改,触发 RDBsave 60 10000 -> 60 秒内有 10000 个 key 被修改,触发 RDB
配置 RDB 文件的存储路径

我们可以在 Redis 的安装目录下看到 dump.rdb 文件,如果没看到,连接到客户端执行一次 shutdown,这个是后面 shutdown 自动触发规则,后续会讲述

配置 RDB 文件的名称

配置 RDB 文件压缩
Redis 默认会使用 LZF 算法对 Redis 的 RDB 文件进行压缩,这会消耗一定的 CPU 计算资源,但是会带来空间上的节省

配置 RDB 文件完整性校验
Redis 默认使用 CRC64 的算法,对 RDB 文件完整性进行校验,以此来保证 RDB 文件的完整

2.3.2 shutdown 触发
shutdown 触发 Redis 的 RDB 持久化机制非常简单,我们在客户端执行 shutdown 即可。

2.3.3 flushall 触发
首先这里一定要特别注意,flushall 是删库跑路,它是清空 dump.rdb 文件,千万千万不要看了博主的文章,跑到公司备份的时候顺手来个 flushall,然后到时候来问候我……,这个 flushall 是为了清空 Redis 数据的同时清空 dump.rdb 文件,要不然重启 Redis 的时候,数据又会恢复到上一次备份的时候的数据,与 flushall 的执行指令含义就冲突了。为了证明这个文件不会保留数据,我特地特地的写个脚本测试一下:
编写一个批量插入的脚本文件
文件赋权


此时查看 dump.rdb

执行 flushall,后再次查看,rbd 文件被清空

2.2 手动触发
手动触发 RDB 持久化的方式可以使用 save 命令和 bgsave 命令,这两个命令的区别如下。save:执行 save 指令,阻塞 Redis 的其他操作,会导致 Redis 无法响应客户端请求,不建议使用。bgsave:执行 bgsave 指令,Redis 后台异步进行快照的保存操作,此时 Redis 仍然能响应客户端的请求。
2.3 RDB 持久化文件的备份
在实际的生产环境中,我们一般不会使用主节点 Master 来进行持久化备份,我们会通过在 Redis 的多个从服务器上进行 RDB 持久化备份,这样是为了对 Redis 数据的多次备份,防止出现网络分区或者部分节点宕机甚至是硬件损坏的情况发生。作为运维或者架构师,李子捌觉得应该要定时定期的通过脚本对 Redis 持久化文件进行转移备份,这样双重保险,更加可靠,万一遇到突发情况,也是多一手解决方案。
3、AOF
3.1 简介
Redis 配置文件中开启,AOF 持久化方案进行备份时,客户端所有请求的写命令都会被追加到 AOF 缓冲区中,缓冲区中的数据会根据 Redis 配置文件中配置的同步策略来同步到磁盘上的 AOF 文件中,同时当 AOF 的文件达到重写策略配置的阈值时,Redis 会对 AOF 日志文件进行重写,给 AOF 日志文件瘦身。Redis 服务重启的时候,通过加载 AOF 日志文件来恢复数据。

3.2 AOF 配置
3.2.1 基本配置
AOF 默认不开启,默认为 appendonly no,开启则需要修改为 appendonly yes

AOF 配置文件的名称默认为 appendonly.aof

配置文件的地址可以通过在 redis 客户端执行 config get dir 获取,其保存路径与 RDB 一致

3.2.2 同步频率配置
AOF 日志是以文件的形式存在的,当程序对 AOF 日志文件进行写操作时,实际上将内容写到了内核为文件描述符分配的一个内存缓冲区中,随后内核会异步的将缓冲区中的数据刷新到磁盘中。如果缓冲区中的数据没来得及刷回磁盘时,服务器宕机了,这些数据就会丢失。因此 Redis 通过调用 Linux 操作系统的 glibc 提供的 fsync(int fid)来将指定文件的内容强制从内核缓冲区刷回磁盘,以此来保证缓冲区中的数据不会丢失。不过这是一个 IO 操作,相比 Redis 的性能来说它是非常慢的,所以不能频繁的执行。Redis 配置文件中有三种刷新缓冲区的配置:appendfsync always 每次 Redis 写操作,都写入 AOF 日志,这种配置理论上 Linux 操作系统扛不住,因为 Redis 的并发远远超过了 Linux 操作系统提供的最大刷新频率,就算 Redis 写操作比较少的情况,这种配置也是非常耗性能的,因为涉及到 IO 操作,所以这个配置基本上不会用
appendfsync everysec 每秒刷新一次缓冲区中的数据到 AOF 文件,这个 Redis 配置文件中默认的策略,兼容了性能和数据完整性的折中方案,这种配置,理论上丢失的数据在一秒钟左右
appendfsync noRedis 进程不会主动的去刷新缓冲区中的数据到 AOF 文件中,而是直接交给操作系统去判断,这种操作也是不推荐的,丢失数据的可能性非常大。

注意要刷新缓冲区的数据到磁盘需要将如下配置,配置为 no,不是 yes
3.2.3 AOF 修复功能
AOF 持久化机制正常恢复与 RDB 持久化机制的恢复是一样的,都只需要将备份文件放置到 Redis 的工作目录下,Redis 启动时就会自动的加载。AOF 持久化机制提供了 AOF 文件异常时恢复的功能,这个功能在 AOF 文件损坏的场景中经常被使用到。
测试,清空 Redis 服务中的数据

写入数据

AOF 日志文件每秒会被刷新一次数据,此时数据已经写入了 appendonly.aof 文件

打开文件我们可以非常清除的阅读 AOF 的文件内容,看到 Redis 的指令序列

此时人为的进行数据破坏

再次启动发现无法启动(我配置的别名启动)

执行 redis-check-aof --fix ../appendonly.aof 对 AOF 日志文件进行修复

修复过程中会有部分数据丢失

连接客户端查看数据

3.2.4 AOF 重写
前面提到 AOF 的缺点时,说过 AOF 属于日志追加的形式来存储 Redis 的写指令,这会导致大量冗余的指令存储,从而使得 AOF 日志文件非常庞大,比如同一个 key 被写了 10000 次,最后却被删除了,这种情况不仅占内存,也会导致恢复的时候非常缓慢,因此 Redis 提供重写机制来解决这个问题。Redis 的 AOF 持久化机制执行重写后,保存的只是恢复数据的最小指令集,我们如果想手动触发可以使用如下指令:
Redis4.0 后的重写使用的是 RDB 快照和 AOF 指令拼接的方式,在 AOF 文件的头部是 RDB 快照的二进制形式的数据,尾部是快照产生后发生的写入操作的指令。由于重写 AOF 文件时,会对 Redis 的性能带来一定的影响,因此也不能随便的进行自动重写,Redis 提供两个配置用于自动进行 AOF 重写的指标,只有这两个指标同时满足的时候才会发生重写:

auto-aof-rewrite-percentage 100:指的是当文件的内存达到原先内存的两倍 auto-aof-rewrite-min-size 64mb:指的是文件重写的最小内存大小
AOF 重写流程如下:
bgrewriteaof 触发重写,判断是否存在 bgsave 或者 bgrewriteaof 正在执行,存在则等待其执行结束再执行
主进程 fork 子进程,防止主进程阻塞无法提供服务,类似 RDB
子进程遍历 Redis 内存快照中数据写入临时 AOF 文件,同时会将新的写指令写入 aof_buf 和 aof_rewrite_buf 两个重写缓冲区,前者是为了写会旧的 AOF 文件,后者是为了后续刷新到临时 AOF 文件中,防止快照内存遍历时新的写入操作丢失
子进程结束临时 AOF 文件写入后,通知主进程
主进程会将上面 3 中的 aof_rewirte_buf 缓冲区中的数据写入到子进程生成的临时 AOF 文件中
主进程使用临时 AOF 文件替换旧 AOF 文件,完成整个重写过程

4、混合持久化
Redis4.0 后大部分的使用场景都不会单独使用 RDB 或者 AOF 来做持久化机制,而是兼顾二者的优势混合使用。其原因是 RDB 虽然快,但是会丢失比较多的数据,不能保证数据完整性;AOF 虽然能尽可能保证数据完整性,但是性能确实是一个诟病,比如重放恢复数据。其日志文件结构如下:

混合持久化通过 aof-use-rdb-preamble yes 开启,Redis 4.0 以上版本默认开启

测试,我们先插入一些 key,然后执行 BGREWRITEAOF 触发 AOF 持久化后,再插入一些 key

此时将会看到如下的效果,验证了混合持久化的方式

5、总结
最后来总结这两者,到底用哪个更好呢?
推荐是两者均开启
如果对数据不敏感,可以选单独用 RDB
不建议单独用 AOF,因为可能会出现 Bug
如果只是做纯内存缓存,可以都不用
Redis 官网是这么介绍的:

看不懂就看下 Redis 中文网的介绍:

Redis 官网关于持久化的介绍
Redis 中文网关于持久化的介绍
版权声明: 本文为 InfoQ 作者【李子捌】的原创文章。
原文链接:【http://xie.infoq.cn/article/22c096413075630c7776ea091】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论