1. RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的 Snapshot 快照,它恢复时是将快照文件直接读到内存里
1.1 备份是如何执行的
Redis 会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件. 整个过程中,主进程是不进行任何 IO 操作的,这就确保了极高的性能. 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效. RDB 的缺点是最后一次持久化后的数据可能丢失.
1.2 fork
- Fork 的作用是复制一个与当前进程一样的进程. 新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
- 在 Linux 程序中,fork() 会产生一个和父进程完全相同的子进程,但子进程在此后多会 exec 系统调用,出于效率考虑,Linux 中引入了“写时复制技术”
- 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程
1.3 RDB 机制
RDB 其实就是把数据以快照的形式保存在磁盘上. 什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来.
RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb
既然 RDB 机制是通过把某个时刻的所有数据生成一个快照来保存,那么就应该有一种触发机制,是实现这个过程. 对于 RDB 来说,提供了三种机制:SAVE
、BGSAVE
、自动化. 我们分别来看一下
1.3.1 SAVE 触发方式
该命令会阻塞当前 Redis 服务器,执行 SAVE
命令期间,Redis 不能处理其他命令,直到 RDB 过程完成为止,具体流程如下:
执行完成时候如果存在老的 RDB 文件,就把新的替代掉旧的.
我们的客户端可能都是几万或者是几十万,这种方式显然不可取.
1.3.2 BGSAVE 触发方式
执行该命令时,Redis 会在后台异步进行快照操作,快照同时还可以响应客户端请求,具体流程如下:
具体操作是 Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束. 阻塞只发生在 fork 阶段,一般时间很短,基本上 Redis 内部所有的 RDB 操作都是采用 BGSAVE
命令,其简化流程图如下所示:
1.3.3 自动触发
自动触发是由我们的配置文件来完成的 (后台还是 BGSAVE
指令),在 redis.conf 配置文件中,里面有如下配置,我们可以去设置:
-
RDB 文件名
在 redis.conf 中配置文件名称,默认为 dump.rdb
-
RDB 文件位置
RDB 文件的保存路径,也可以修改,默认为 Redis 启动时命令行所在的目录下
-
自动保存策略
需要在配置文件中配置定时触发条件
-
stop-writes-on-bgsave-error
默认值为 yes,表示当启用了 RDB 且最后一次后台保存数据失败,Redis 是否停止接收数据.
开启后会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了. 如果Redis重启了,那么又可以重新开始接收数据了.
-
rdbcompression
压缩文件默认值是 yes, 对于存储到磁盘中的快照,可以设置是否进行压缩存储
如果是的话,Redis 会采用 LZF 算法进行压缩
-
rdbchecksum
检查完整性在存储快照后,还可以让 Redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10% 的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能.
推荐开启
1.4 RDB 的备份恢复
-
关闭 Redis
-
先把备份的 RDB 文件拷贝到工作目录下
cp dump2.rdb dump.rdb
-
启动 Redis,备份数据会直接加载
1.5 RDB 的优势和劣势
优势:
- RDB 文件紧凑,全量备份,非常适合用于进行备份和灾难恢复
- 生成 RDB 文件的时候,Redis 主进程会 fork 一个子进程来处理所有保存工作,主进程不需要进行任何磁盘 IO 操作
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快
劣势:
- RDB 快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑. 当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据.
2. AOF
以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,Redis 启动之初会读取该文件重新构建数据,换言之,Redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作.
2.1 AOF 持久化流程
-
客户端的请求写命令会被 append 追加到 AOF 缓冲区内
-
AOF 缓冲区根据 AOF 持久化策略将操作同步到磁盘的 AOF 文件中
-
AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件 rewrite 重写,压缩 AOF 文件容量
-
Redis 服务重启时,会重新加载 AOF 文件中的写操作达到数据恢复的目的
2.2 开启 AOF
AOF 默认是不开启的,需要在配置文件中配置开启
AOF 文件的保存路径默认和 RDB 文件一致
2.3 AOF 三种持久化策略
-
appendfsync always
始终同步,每次 Redis 的写入都会立刻记入日志;性能较差但数据完整性比较好
-
appendfsync everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失
-
appendfsync no
Redis 不主动进行同步,把同步时机交给操作系统
2.4 AOF 重写
因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大. 举个例子, 如果你对一个计数器调用了 100 次 INCR
, 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET
命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的.
为了处理这种情况, Redis 支持一种有趣的特性: 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF
命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令.
-
BGREWRITEAOF
命令Redis
BGREWRITEAOF
命令用于异步执行一个 AOF(AppendOnly File)文件重写操作,重写会创建一个当前 AOF 文件的体积优化版本.即使
BGREWRITEAOF
执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在BGREWRITEAOF
成功之前不会被修改.
AOF 重写由 Redis 自行触发,BGREWRITEAOF
仅仅用于手动触发重写操作.
具体内容:- 如果一个子 Redis 是通过磁盘快照创建的,AOF 重写将会在 RDB 终止后才开始保存. 这种情况下
BGREWRITEAOF
任然会返回 OK 状态码. 从 Redis 2.6 起你可以通过INFO
命令查看 AOF 重写执行情况. - 如果正在执行的 AOF 重写返回一个错误,AOF 重写将会在稍后一点的时间重新调用.
- 如果一个子 Redis 是通过磁盘快照创建的,AOF 重写将会在 RDB 终止后才开始保存. 这种情况下
-
配置自动重写
-
auto-aof-rewrite-percentage
触发 AOF 文件执行重写的增长率,也就是文件大小增长了百分之多少就要重写
-
auto-aof-rewrite-min-size
触发 AOF 文件执行重写的最小尺寸
-
no-appendfsync-on-rewrite
:
同时在执行 BGREWRITEAOF
操作和主进程写 AOF 文件的操作,两者都会操作磁盘,而 BGREWRITEAOF
往往会涉及大量磁盘操作,这样就会造成主进程在写 AOF 文件的时候出现阻塞的情形,现在 no-appendfsync-on-rewrite
参数出场了,如果该参数设置为 no
,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题. 如果设置为 yes
呢?这就相当于将 appendfsync
设置为 no
,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候 Redis 挂掉,就会丢失数据. 丢失多少数据呢?在 Linux 操作系统的默认设置下,最多会丢失 30 s 的数据.
AOF 重写触发的条件:
🍡 文件大小超过 auto-aof-rewrite-min-size
🍡 文件大小相较上次重写完的文件大小,增长率超过 auto-aof-rewrite-percentage
AOF 重写流程:
(1)BGREWRITEAOF
触发重写,判断是否当前有 BGSAVE
或 BGREWRITEAOF
在运行,如果有,则等待该命令结束后再继续执行
(2)主进程 fork 出子进程执行重写操作,保证主进程不会阻塞
(3)子进程遍历 Redis 内存中数据到临时文件,客户端的写请求同时写入 aof_buf 缓冲区和 aof_rewrite_buf 重写缓冲区,保证原 AOF 文件完整以及新 AOF 文件生成期间的新的数据修改动作不会丢失
(4.1)子进程写完新的 AOF 文件后,向主进程发信号,父进程更新统计信息
(4.2)主进程把 aof_rewrite_buf
中的数据写入到新的 AOF 文件
(5)使用新的 AOF 文件覆盖旧的 AOF 文件,完成AOF重写
2.5 AOF 和 RDB 优先级
AOF 和 RDB 同时开启,系统默认取 AOF 的数据(数据不会存在丢失)
2.6 AOF 的备份恢复
AOF 的备份机制和性能虽然和 RDB 不同,但是备份和恢复的操作同 RDB 一样,都是拷贝备份文件,需要恢复时再拷贝到 Redis 工作目录下,启动系统即加载
- 正常恢复
- 修改默认的
appendonly no
,改为yes
- 将有数据的 AOF 文件复制一份保存到对应目录
- 恢复:重启 Redis 然后重新加载
- 修改默认的
- 异常恢复
- 修改默认的
appendonly no
,改为yes
- 如遇到 AOF 文件损坏,通过
/usr/local/bin/redis-check-aof --fix appendonly.aof
进行恢复 - 备份被写坏的 AOF 文件
- 恢复:重启 Redis,然后重新加载
- 修改默认的
3. RDB 和 AOF 的抉择
3.1 RDB 和 AOF 对比
RDB | AOF | |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
3.2 如何选择使用哪种持久化方式
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化
有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快