文章目录
- 1.RDB
- 1)基本介绍
- 2)自动触发
- 3)手动触发
- 4)RDB文件
- 5)优点缺点
- 2.AOF
- 1)基本介绍
- 2)使用方式
- 3)工作流程
- 4)重写机制
- 5)AOF文件
- 6)优点缺点
- 3.RDB + AOF
我们都知道,redis 是一个基于内存的数据库。基于内存的好处是访问速度快,缺点是“不持久”——当数据库重启时内存中原先的数据全都会被清空。然而小孩子才做选择,redis 说我全都要:当插入一份新的数据时,在内存中会存储一份,在磁盘中也会存储一份(当然具体怎么写 redis 有自己的策略,从而保证足够的效率)
当要访问数据时,直接从内存中获取;当 redis 重启时,可以根据磁盘中的备份来恢复。虽然会消耗更多的磁盘空间,然而磁盘资源在计算机世界里可以说是最廉价的了,这样的开销并不会带来多大的成本。
Redis官网中列出了如下四种持久化策略:
1.RDB
1)基本介绍
RDB(Redis Database)是 redis 中默认使用的持久化策略,它的思想是定期对 redis 进行一次全量备份,备份数据是二进制的方式存储的,其存储路径由配置文件 redis.conf 指定。
RDB持久化过程分为自动触发和手动触发,下面分别介绍
2)自动触发
自动触发有三种情况:
-
在m秒内数据集发生了n次修改则自动触发。m和n的具体数值由配置文件决定,在我当前的配置文件下有3中选项
注意,m与n是并且的关系,只有同时满足才会被触发。例如上面的触发条件翻译为:距离上一次备份过去 60s 时,至少发生了 10000 次修改则触发 rdb 备份,另外两条同理。如果你想禁止自动生成 rdb 快照,你可以这样修改配置文件:save ""
-
从节点进行全量复制操作时,主节点自动进行RDB持久化,随后将RDB文件内容发送给从结点
-
执行shutdown命令关闭Redis时,执行RDB持久化。所以说正常退出 Redis 服务器前都会对当前的数据进行一次备份
3)手动触发
触发命令:
手动出发 RDB 的指令有两个:
- save:阻塞当前 Redis 服务端,直到RDB过程完成为止。对于内存比较大的实例,可能会造成 Redis 服务端的长时间阻塞,影响正常的业务请求,因此不推荐使用
- bgsave:Redis 通过 fork() 指令创建子进程,由子进程完成 RDB 的持久化过程。阻塞过程只会发生在 fork 阶段,但是得益于写时拷贝机制,创建子进程的时间一般很短,影响不大,因此推荐使用
执行流程:
-
执行
bgsave
命令后,Redis 父进程会首先检查是否有正在执行 RDB/AOF 备份的子进程,如果有 bgsave 就立刻返回,间隔时间这么短,再开一个子进程备份的意义不大。 -
父进程执行 fork 创建子进程。fork过程中父进程可能会被阻塞,最近一次的fork操作耗时可以在redis 服务端使用
info
指令查看,单位为微秒。
-
父进程 fork() 完成后,就可以继续接受客户端的业务请求,而将备份的任务交给子进程完成
-
子进程会向一个临时的快照文件写入 redis 备份数据,当备份完成时,会删除历史的 dump.rdb 并将该快照文件重命名为 dump.rdb,从而保证了自始至终只有一份 rdb 备份文件
-
子进程通过信号通知父进程 rdb 备份工作完成
实验验证:
打开 /var/lib/redis (具体路径因人而异,见配置文件)下的 dump.rdb 文件,正如我们所预期的一样,文件以二进制的形式存储,所以呈现出来的都是人眼不能识别的乱码:
使用 stat
指令记录下此时的 innode 编号
执行 bgsave 手动完成 rdb 备份时,根据 Inode 编号我们就可以发现文件已经被替换了。
4)RDB文件
保存:
RDB文件保存在 dir 配置指定的目录(默认/var/lib/redis/)下,文件名通过 dbfilename配置(默认dump.rdb)指定。
可以通过执行 config set dir{newDir}
和 config set dbfilename{newFilename}
运行期间动态执行,当下次运行时RDB文件会保存到新目录
压缩:
Redis默认采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数 config set rdbcompression{yes|no}
动态修改
校验:
dump.rdb 里面的数据不要乱改,修改后可能看不出什么影响,也可能会导致数据错误,甚至有可能导致 redis 服务端启动失败。
-
我们手动向 dump.rdb 文件追加数据,然后重启 redis 服务端,观察结果。使用 keys * 发现似乎没什么影响
-
我们再尝试在文件中间修改,然后重启 redis 服务端,观察结果:
现在好了,连 redis 客户端也连不上了,看看日志文件怎么说(日志文件路径同样可以在配置文件中找到,对应 logfile 配置项)
日志说的很明白了,都是 RDB 文件格式不对惹的祸。当 dump.rdb 被恶意修改时,其结果是不可预期的。对此,我们可以使用 redis 提供的 rdb 文件格式检查工具进行检查:
5)优点缺点
优点:
- RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照,非常适用于备份,全量复制等场景。非常适合灾难恢复,因为它是一个可以传输到远程数据中心的单一紧凑文件
- RDB 持久化策略可以最大化 Redis 的性能,因为父进程从不需要执行磁盘I/O或类似操作,一切的 IO 工作都交给了子进程完成
- 与AOF相比,RDB允许使用大数据集更快地重新启动。因为 RDB 是以二进制的方式存储数据,而 AOF 是以文本的形式记录对 redis 的各种数据操作
缺点:
- RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行 fork 创建子进程,属于重量级操作,频繁执行成本过高。 因此如果 Redis 服务端在两次备份中间异常退出了(正常退出会自动备份),那么退出前的新插入的数据就得不到持久化
- RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个RDB版本,兼容性可能有风险
2.AOF
1)基本介绍
前面谈到,RDB 最大的缺点是不能实时持久化保存数据,在两次快照之间,实时数据可能会因为 Redis 异常退出而丢失。而 AOF 则可以较好的处理实时性的问题。
AOF类似于mysql中的binlog,它会以独立日志的方式记录 Redis 服务端收到的每一条写入操作,重启时再重新执行AOF文件中的各种命令达到恢复数据的目的。 AOF 解决了数据的实时性问题,因此已经成为了 Redis 持久化的主流方式。
2)使用方式
Redis 中默认采用的持久化策略为 RDB,开启 AOF 功能需要设置配置: appendonly yes
,当RDB 与 AOF 可以同时设置,但如果开启 AOF ,Redis 在启动时就会从 AOF 中加载数据
3)工作流程
具体流程:
- Redis 客户端输入的所有指令都会被追加到 aof_buf 缓冲区中
- AOF 缓冲区根据策略定期将数据刷新到磁盘
- 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,以达到数据压缩的目的
- 当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复
缓冲区:
AOF过程中为什么需要aof_buf这个缓冲区?Redis使用单线程响应命令,如果每次写AOF文件都直接同步硬盘,性能从内存的读写变成IO读写,必然会下降。先写入缓冲区可以有效减少IO次数。同时,Redis还可以提供三种缓冲区同步策略,让用户根据自己的需求做出合理的平衡 :
- no:由OS控制 fsync 频率。效率最高但安全性最差
- always:每一次写入都会立即进行 fsync 刷盘。效率最低,安全性最高
- everysec:每秒执行一次 fsync。兼顾安全性和效率,最常使用
虽然缓冲中的数据在刷盘前也存在丢失的风险,但是相较于 RDB,AOF的刷新频率很快,所以你最多只会丢失 1s,甚至一行命令的数据
4)重写机制
基本认识:
随着命令不断写入AOF,.aof
文件会越来越大,Redis 服务端重启的速度自然越来越慢。为了解决这个问题,Redis引入 AOF 重写机制来压缩文件体积。
重写机制就是去除冗余操作,合并相同操作的过程,虽然听起来很复杂,但实现起来却很简单,因为对于 aof 文件而言,并不需要关心中间的各种过程,只关心最后 Redis 数据库的状态,因此重写的本质是将 Redis 进程内的数据转化为写命令同步到新的 AOF文件
触发条件:
AOF 重写过程有自动触发和手动触发两种方式:
-
自动触发:触发时机由配置文件中的参数决定,两个同样是 “并且” 的关系
-
auto-aof-rewrite-min-size
:表示触发重写时AOF的最小文件大小,默认为64MB -
auto-aof-rewrite-percentage
:代表当前AOF占用大小相比较上次重写时增加的比例,默认为 100%,意味着至少要是上次重写体积的两倍才触发
-
-
手动触发:调用
bgrewriteaof
命令
重写流程:
- 如果当前有进程已经在执行 AOF 重写,则返回一个 error。如果有进程正在执行 bgsave 命令,则 bgsave 完成后重写才会开始
- 父进程创建子进程,由子进程完成重写任务
- 父进程fork之后,继续响应其他命令:
- 所有修改操作写入 AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证旧 AOF 文件机制正确
- 同时 fork 之后的数据也要写一份到 aof_rewrite_buf 缓冲区中以保证数据的一致性(后面具体解释)
- 子进程根据内存快照,将数据以命令的形式合并到新的AOF文件中
- 子进程完成重写工作后:
- 子进程向父进程发送信号,通知父进程重写工作完成
- 父进程把 aof_rewrite_buf 内临时保存的命令追加到新 AOF 文件中
- 用新 AOF 文件替换老的 AOF 文件 (实验方法同上)
重写缓冲区
为什么需要重写缓冲区,看下面这个例子:
时间 | 父进程 | 子进程 |
---|---|---|
t1 | execute command “set key1 1” | |
t2 | execute command “set key2 2” | |
t3 | Create subprocess, execute AOF file rewrite | Start AOF file rewrite |
t4 | execute command “set key1 11” | execute the rewrite operation |
t5 | execute command “set key2 22” | execute the rewrite operation |
t6 | …… | Complete AOF rewrite |
我们知道,进程具有独立性,子进程创建后就和父进程在数据上独立了,这就意味着子进程只会记录上图中 t3 时刻 Redis 内存中的数据。因此父进程额外开辟了一块缓冲区用于记录fork 后父进程收到的请求,在子进程完成重写后再将这块缓冲区追加到 new AOF 文件的结尾,就可以保证数据的一致性。
AOF缓冲区
为什么 fork 后父进程还要向 AOF 缓冲区写入数据呢?对于安全问题,我们往往要考虑到各种极端的情况。如果子进程还没有完成重写 Redis 服务端就异常退出了,抑或是主机掉电了,那么新 AOF 文件的内容肯定是不完整的,内存中的 aof_rewrite_buf 也已经丢失,这就意味着 fork 后插入的数据都丢失了。
因为即使 fork 后,父进程仍然要向 aof_buf 写入,并按照刷新策略定期刷新到磁盘,从而保证数据的安全
5)AOF文件
AOF命令写入的内容直接是文本协议格式。例如set hello world这条命令,在AOF文件中会追加如下文本 :
此处遵守Redis格式协议,Redis选择文本协议可能的原因:文本协议具备较好的兼容性;实现简单;具备可读性。 对于 AOF 文件,redis 同样提供了格式检查工具 redis-check-aof,这里不再重复演示。
6)优点缺点
优点:
- AOF 策略相比于 RDB 数据更加可靠。everysec 策略下,你最多损失 1s 的数据
- AOF 的重写机制在保证数据安全的前提下,避免了 AOF 文件太臃肿
- AOF 文件以文本的方式包含了各种 operations,具有较好的可读性。你甚至可以轻松的导出 AOF 文件,例如你不小心的使用
FLUSHALL
删除所有数据,你也可以根据 AOF 文件恢复出FLUSHALL
之前的所有数据,在没有 rewrite 的前提下。
缺点:
- AOF 文件通常比 RDB 文件体积更大
- AOF 持久化策略通常比 RDB 策略效率低。一般来说,在fsync设置为每秒一次的情况下,性能仍然很高,而在禁用fsync的情况下即使在高负载下,它应该与RDB一样快
3.RDB + AOF
Redis引入了“混合持久化”的方式,结合了 AOF + RDB 的优点:
-
按照 AOF 的方式记录每一个请求
-
在触发 AOF 重写后,就把当前内存的状态按照 RDB 二进制文件的格式写入到新的 aof 文件中(对上一个文件重写后的结果)
-
后续再进行操作仍然是以 aof 文本的方式追加到 aof 文件末尾
该功能默认开启,对应配置文件中的参数如下: