目录
为什么要进行持久化?
因为 redis 是一个内存数据库,而在内存存储的数据又是不持久的,所以就需要将 redis 的数据进行持久化,将数据存储在硬盘上。但是 redis 又是以效率高(速度快) 为核心,所以 redis 为了保证速度快,肯定还是得从内存中读取数据,但是又要将数据持久化,所以就可以既在内存中存储数据,硬盘上也存储数据。所以当往 redis 中插入一个数据时,需要将数据存储在内存和硬盘中。
redis 为实现持久化,提供了两种策略。第一种是 RDB(Redis DataBase),第二种是 AOF(Append Only File)。
1. RDB
不同的策略应对了不同的场景,而 RDB 的设计理念就是定期备份。
RDB 定期把内存的所有数据都写入硬盘中,生成一个 "快照"。
也就是将当前内存中的数据赶紧拍个照片,生成一个文件保存到硬盘中。
后面 redis 服务重启了,也可以根据这个 "快照" 将刚刚的数据恢复回来。
触发 RDB 有两种方式,一种是手动触发,一种是自动触发。
1.1 手动触发 RDB
可以通过 save 和 bgsave 命令来触发 rdb 。
因为 redis 是单线程的,如果使用 save 命令的话,这一个线程就会去执行 save 命令,可能会导致 redis 阻塞,后面的命令都无法被执行,只有当 save 命令完成后,redis 才能继续执行后面的命令,一般来说不推荐使用。
而 bgsave 命令,则是在后台完成,不会阻塞 redis 处理其他命令。
1.2 bgsave 的执行流程
redis 收到 bgsave 命令后,先会看看是否已经有子进程正在执行 bgsave 命令,如果是,就会直接返回,如果不是,则会调用 linux 自带的 fork 方法创建出一个子进程(子进程里面的内容跟父进程完全一致,相当于 "克隆体"),此时子进程就可以根据内存生成快照文件,父进程则继续执行其他命令,当子进程完成生成快照文件,并将原有的 rdb 文件进行替换成新的快照文件后,会通知父进程说我干完了,然后子进程就可以销毁了。
流程图如下:
1.3 RDB 文件的处理
redis 是默认开启了 rdb 的。
redis 生成的 rdb 文件,默认是放在 /var/lib/redis 的目录下,也可以在 redis 配置文件中进行设置。
dump.rdb 是二进制的文件,把内存中的数据,以压缩的形式,保存在这个二进制文件中。(可以节省空间)
后续 redis 启动的时候,就会去尝试加载 dump.rdb 的内容,如果发现格式错误,就可能加载数据失败,而 redis 也提供了检查格式的工具。
虽然 rdb 持久化可以执行多次,但是 dump.rdb 文件始终都是只有一个的,因为在 rdb 持久化时,会生成新的 rdb 文件,这个新的 rdb 会替代旧 的 rdb 文件,也就是会将旧的 rdb 文件删除,将新的 rdb 文件名改成 dump.rdb。
可以用 vim 打开 dump.rdb 看一看,但是里面数据最好不要乱改。
rdb 的触发时机一个是上面说的手动触发,另一个是自动触发。
1.4 自动触发 RDB
自动触发是根据 redis 的配置文件的配置项设定的,可以进行修改。
可以通过 cd /etc/redis/ 跳转到 redis 配置文件的目录,然后用 vim redis.conf 打开配置文件。
以上这些都可以配置,但是有一个基本原则,生成快照文件,是一个成本比较高的,所以不能频繁的生成快照文件,所以就会导致内存中的数据可能回合 rdb 文件中的数据有偏差。想要能够保持实时修改就能实时保存的话,就可以用 AOF。
1.5 RDB 的优缺点
优点就是 RDB 是一个压缩的二进制文件,占用体积小。非常适用于备份,全量复制等场景。
而且 redis 读取 RDB 文件来恢复数据的速度远远大于 AOF 的方式。
缺点就是 RDB 无法做到实时持久化,频繁生成快照的成本过高,而且不同版本的 redis 的 rdb 文件可能不兼容。
2. AOF
AOF (Append Only File) 持久化,以文本方式来记录 redis 每次执行的命令,重启时通过重新执行 AOF 文件的命令来达到恢复数据的目的。解决了 redis 实时保存数据的问题。
AOF 的工作流程是:命令写入(append),文件同步(sync),文件重写(rewrite),重启加载(load)。
如下图:
AOF 默认是关闭状态,可以通过修改配置文件来手动开启。
先找到 redis 的配置文件,然后用 vim 来打开。
要注意,如果开启了 AOF ,RDB 就不会生效了,因为 AOF 的优先级比 RDB 要高,毕竟 AOF 是实时保存数据。
这里的实时保存,不是用户修改 redis,就会立刻把命令写入 aof 文件中,而是有个缓冲区,它会先写入到内存中的缓冲区,积累一波后,才会按顺序写入 aof 文件的末尾中,这样就大大降低了写硬盘的次数。
这就跟我们嗑瓜子一样,假如垃圾桶不在身边,不可能嗑完一个瓜子,就立刻起身走到垃圾桶那里去把瓜子皮扔了,然后继续回来嗑瓜子,又嗑完一个瓜子,又立马去扔瓜子皮,而是一般来说,会先把嗑完的瓜子皮放在手上(就相当于缓冲区),等到手掌里的瓜子皮放不下了,再去一口气扔完瓜子皮,这样做的话效率会比较高。
那可以考虑一种极端的情况,假设存储在缓冲区的数据,还没来得及写入硬盘中,主机就掉电挂了,那此时,因为缓冲区的数据没能写入硬盘中,所以数据就会丢了。
而 redis 明显也考虑到了这种情况,所以提供了三种缓冲区的刷新策略:
2.1 AOF 缓冲区三种刷新策略
2.2 AOF 的重写(rewrite)机制
当越来越多的命令被写入 aof 文件时,aof 文件的体积势必会变得越来越大,而此时,redis 提供了解决这个问题的方案,那就是重写机制 (rewrite)。重写机制可以压缩 aof 文件的体积。
aof 文件,可能会记录大量的冗余操作,比如:
aof 文件关注的不是数据增删查改的过程,而是关注数据最终的结果(因为 redis 重启服务器时,会去读取 aof 文件来恢复数据),所以就需要剔除 aof 文件中的冗余操作,让 aof 文件的体积变小。
也是分为两种情况,自动触发重写和手动触发重写。
手动触发重写:通过 bgrewriteaof 命令来重写。
自动触发重写:可以通过修改 redis 的配置文件来自定义重写时机。
2.3 AOF 的重写流程
其实跟 RDB 的流程差不多。
redis 收到 bgrewriteaof 命令后,会调用 fork 方法创建出一个子进程(与当前父进程一摸一样,但是之后的父进程修改,子进程是不知道的),子进程就会去保存当前内存中的数据(内存中数据的状态,就相当于是把 AOF 文件整理完毕后的样子),以 AOF 的格式(文本格式)写入到一个新的 AOF 文件中,与此同时,父进程也会继续处理其它的命令,并将命令存储在 aof_buf(缓冲区) 以及 aof_rewrite_buf(实时保存) 中,也会将 aof_buf 的数据写入旧的 AOF 文件中,当子进程将内存中的数据全部写入到新 AOF 文件后,就会信号通知父进程说我干完了,子进程旧销毁了,然后父进程就会把 aof_rewrite_buf 里的内容写入到新的 AOF 文件中,然后再用新的 AOF 文件替换旧的 AOF 文件。
如果在执行 bgrewriteaof 时,当前 redis 已经在进行 aof 重写了,那就会直接返回。
如果在执行 bgrewriteaof 时,当前 redis 正在进行生成 rdb 快照,那就会先等待完成 rdb 快照,再来执行 aof 重写。
为什么父进程在 fork 子进程后,还要继续写旧的 AOF 文件,直接写新的 AOF 文件不就好了吗?
考虑到极端情况,到 AOF 重写执行到一半时,如果主机掉电了,那么这个缓冲区里的数据就丢失了,而新 aof 的文件还不完整,但是因为之前重写时还在继续往旧的 aof 文件写数据,所以这部分数据就不会丢失,也就是说数据还是能保持完整性。
2.4 AOF 的优缺点
优点:能够实时备份数据,保证了数据的准确性,相较于 rdb 需要生成快照文件来说,aof 写入性能更高(写入文件末尾)。
缺点:因为是实时备份,频繁的写入文件,还是会有开销的,性能相较于 rdb 来说,会降低,而且在 redis 加载 aof 文件时,相对于 rdb 文件来说会比较慢。
因为 AOF 是以文本的方式来进行写入的文件的,所以在后续 redis 加载文件时,成本会比较高(因为会进行字符串的切割之类的,在加载时不如二进制的直接读取来得快),所以 redis 就引入了混合持久化的方式,结合了 RDB 和 AOF 的优点。
3. 混合持久化
混合持久化结合了 RDB 和 AOF 的优点,可以通过配置文件来开启混合持久化。
默认是打开状态。
开启混合持久化之后,它会按照 aof 的方式,每一个命令都记录入文件,在触发 aof 重写之后,就会把当前内存状态按照 rdb 的二进制格式,写入到新的 aof 文件中,后续再进行的操作,还是按照 aof 文本的格式追加到文件后面。
这样文件体积不会那么大,同时也保证了 redis 加载文件的效率。