一、概述
Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis提供了一系列持久化选项:
RDB(Redis DataBase:内存快照):将某一时刻的内存数据,以二进制的方式写入磁盘;AOF(Append Only File:增量日志):每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;- 混合持久化方式(RDB + AOF):
Redis 4.0新增的方式,集成了AOF和RBD的优点; - 无持久化:完全禁用持久化。
默认使用
rdb,redis 4.0以上默认使用混合持久化方式
二、RDB持久化
RDB (Redis DataBase),指的是在指定的时间间隔内将内存中的数据集快照写入磁盘,RDB是内存快照(内存数据的二进制序列化形式)的方式持久化,每次都是从Redis中生成一个快照进行数据的全量备份。
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配置文件中,默认注释了下面三行数据,通过配置规则来触发RDB的持久化,需要开启或者根据自己的需求按照规则来配置。
yaml
# RDB核心规则配置
# 满足条件就将内存中的数据同步到硬盘中。若不想用RDB方案,可以把save ""的注释打开,下面三个注释
# save ""
# save <指定时间间隔> <执行指定次数更新操作>
save 3600 1 # 3600秒内有1个key被修改,触发RDB
save 300 100 # 300秒内有100个key被修改,触发RDB
save 60 10000 # 60秒内有10000个key被修改,触发RDB
- 配置RDB文件的存储路径
bash
# 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /usr/local/redis/var
我们可以在Redis的安装目录下看到dump.rdb文件,如果没看到,连接到客户端执行一次shutdown,这个是后面shutdown自动触发规则,后续会讲述。

- 配置
RDB文件的名称

lua
# 指定本地数据库文件名,一般采用默认的 dump.rdb
dbfilename dump.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命令是清空redis内存中的数据,并且同时清空dump.rdb文件。所以这个命令就相当于删库跑路,此处只是说明该命令会触发rdb,实际使用中千万不要执行。
此时查看dump.rdb。

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

2.4 手动触发
手动触发RDB持久化的方式可以使用save命令和bgsave命令,这两个命令的区别如下:
save:执行save指令,阻塞Redis的其他操作,会导致Redis无法响应客户端请求,不建议使用。bgsave:执行bgsave指令,Redis后台异步进行快照的保存操作,此时Redis仍然能响应客户端的请求。
2.5 RDB持久化文件的备份
在实际的生产环境中,我们一般不会使用主节点Master来进行持久化备份,我们会通过在Redis的多个从服务器上进行RDB持久化备份,这样是为了对Redis数据的多次备份,防止出现网络分区或者部分节点宕机甚至是硬件损坏的情况发生。
应该要定时定期的通过脚本对
Redis持久化文件进行转移备份,这样双重保险,更加可靠,万一遇到突发情况,也是多一手解决方案。
2.6 RDB优缺点
优点:
- 存储紧凑,节省内存空间;
- 恢复速度非常快;
- 适合全量备份、全量复制的场景,经常用于灾难恢复(对数据的完整性和一致性要求相对较低的场合)。
缺点:
- 容易丢失数据,容易丢失两次快照之间
Redis服务器中变化的数据; RDB通过fork子进程对内存快照进行全量备份,是一个重量级操作,频繁执行成本高;fork子进程,虽然共享内存,但是如果备份时内存被修改,最大可能膨胀到2倍大小。
三、AOF持久化
AOF (Append Only File)是把所有对内存进行修改的指令(写操作)以独立日志文件的方式进行记录,重启时通过执行AOF文件中的Redis命令来恢复数据。AOF能够解决数据持久化实时性问题,是现在Redis持久化机制中主流的持久化方案(后续会谈到4.0以后的混合持久化)。
3.1 简介
Redis配置文件中开启,AOF持久化方案进行备份时,客户端所有请求的写命令都会被追加到AOF缓冲区(service.aof_buf)中,缓冲区中的数据会根据Redis配置文件中配置的同步策略来同步到磁盘上的AOF文件中,同时当AOF的文件达到重写策略配置的阈值时,Redis会对AOF日志文件进行重写,给AOF日志文件瘦身。Redis服务重启的时候,通过加载AOF日志文件来恢复数据。

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

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

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

3.2.1 同步频率配置
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 no
Redis进程不会主动的去刷新缓冲区中的数据到AOF文件中,而是直接交给操作系统去判断,这种操作也是不推荐的,丢失数据的可能性非常大。

| 写回策略 | 写回时机 | 优点 | 缺点 |
|---|---|---|---|
| always | 同步写回 | 可靠性高、最大程度保证数据不丢失 | 每个写命令都要写回硬盘,性能开销大 |
| everysec | 每秒写回 | 性能适中 | 宕机时会丢失1秒内的数据 |
| no | 由操作系统控制写回 | 性能好 | 宕机时丢失的数据可能很多 |
3.2.2 刷新缓冲区
在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no。
如果对延迟要求很高的应用,这个字段可以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。
设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。Linux的默认fsync策略是30秒,可能丢失30秒数据。
注意要刷新缓冲区的数据到磁盘需要将如下配置,配置为
no,不是yes。
perl
no-appendfsync-on-rewrite no
3.3 AOF修复功能
AOF持久化机制正常恢复与RDB持久化机制的恢复是一样的,都只需要将备份文件放置到Redis的工作目录下,Redis启动时就会自动的加载。AOF持久化机制提供了AOF文件异常时恢复的功能,这个功能在AOF文件损坏的场景中经常被使用到。
测试
清空Redis服务中的数据。

写入数据。

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

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

此时人为的进行数据破坏。

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

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

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

连接客户端查看数据。

3.4 AOF重写
前面提到AOF的缺点时,说过AOF属于日志追加的形式来存储Redis的写指令,这会导致大量冗余的指令存储,从而使得AOF日志文件非常庞大,比如同一个key被写了10000次,最后却被删除了,这种情况不仅占内存,也会导致恢复的时候非常缓慢,因此Redis提供重写机制来解决这个问题。Redis的AOF持久化机制执行重写后,保存的只是恢复数据的最小指令集,我们如果想手动触发可以使用如下指令:
bgrewriteaof
Redis 4.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文件,完成整个重写过程。

3.5 AOF优缺点
优点:
- 数据的备份更加完整,丢失数据的概率更低,适合对数据完整性要求高的场景;
- 日志文件可读,
AOF可操作性更强,可通过操作日志文件进行修复。
缺点:
AOF日志记录在长期运行中逐渐庞大,恢复起来非常耗时,需要定期对AOF日志进行重写(瘦身处理);- 恢复备份速度比较慢;
- 同步写操作频繁会带来性能压力。
四、混合持久化
AOF与RDB对比
| RDB | AOF | |
|---|---|---|
| 持久化方式 | 定时对整个内存做快照 | 记录每一次执行的命令 |
| 数据完整性 | 不完整,两次备份之间会丢失 | 相对完整,取决于刷盘策略 |
| 文件大小 | 会有压缩,文件体积小 | 记录命令,文件体积大 |
| 宕机恢复速度 | 很快 | 慢 |
| 数据恢复优先级 | 低,因为数据完整性不如AOF | 高,因为数据完整性更高 |
| 系统资源占用 | 高,大量CPU和内存消耗 | 低,主要是磁盘IO资源 但AOF重写时会占用大量CPU和内存消耗 |
| 使用场景 | 可以容忍数分钟的数据丢失,追求更快的启用速度 | 对数据安全性要求较高 |
4.1 简介
RDB数据恢复虽然快,但是快照的频率不好把握。频率太低,会丢失比较多的数据,频率太高,会影响性能;AOF虽然能尽可能保证数据完整性,但是性能确实是一个诟病,比如重放恢复数据。
为了集合两者的优点,Redis 4.0提出了混合使用RDB和AOF来做持久化,既保证了Redis重启速度,又降低数据丢失风险。
混合持久化工作在AOF日志重写过程,当开启了混合持久化时,在AOF重写日志时,fork出来的重写子进程会先将与主线程共享的内存数据以RDB方式写入到AOF文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以AOF方式写入到AOF文件,写入完成后通知主进程将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。
也就是说,使用了混合持久化,AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。其日志文件结构如下:

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

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

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

4.3 优缺点
混合持久化优点:
- 混合持久化结合了
RDB和AOF持久化的优点,开头为RDB的格式,使得Redis可以更快的启动,同时结合AOF的优点,有减低了大量数据丢失的风险。
混合持久化缺点:
AOF文件中添加了RDB格式的内容,使得AOF文件的可读性变得很差;- 兼容性差,如果开启混合持久化,那么此混合持久化
AOF文件,就不能用在Redis 4.0之前版本了。
五、总结
最后来总结这两者,到底用哪个更好呢?
- 推荐是两者均开启;
- 如果对数据不敏感,可以选单独用
RDB; - 不建议单独用
AOF,因为可能会出现Bug; - 如果只是做纯内存缓存,可以都不用。
六、拓展
6.1 如何选择合适的持久化方式?
- 如果你的业务场景需要很高的性能,或者宕机之后能够尽快的恢复,而对数据完整性的要求不是那么高,那么可以采用
RDB持久化的方式。 - 如果你的业务场景对数据完整性的要求很高,那么可以采用
AOF的持久化方式,而至于采用那种回写策略,则取决于你对数据完整性的要求程度。 - 如果你的业务场景既要兼顾性能,又注重数据完整性,那么可以采用混合持久化的方式。
- 如果你对数据丢失无所谓,追求性能最大化的情况下,你也可以不使用任何持久化方式。
6.2 Redis持久化数据和缓存怎么做扩容?
- 如果
Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。 - 如果
Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。