高频面试题——Redis 的 RDB 持久化
良心公众号
关注不迷路
01
RDB 持久化背景
在之前的 Redis 的五种基本类型(实战篇)和 Redis 的五种基本类型(原理篇)两篇文章中,我们讨论了 Redis 支持的数据类型和底层原理,这是对于 Redis 这样一个键值对数据库最为基础的。由于 Redis 的自身特性,在日常开发过程中,最常用的场景就是将其作为内存数据库来用。
众所周知,内存 (不包括非易失性内存) 是不可靠的,如果不将数据持久化到磁盘,在服务器进程退出之后,内存中的数据将会丢失。
为了解决上述问题,Redis 提供了 RDB 持久化的能力,该能力可以将 Redis 在内存中的数据持久化到磁盘,从而防止数据丢失。
02
RDB 持久化简介
Redis 的 RDB 持久化是通过将某一时间点的数据库状态保存到 RDB 文件 (经过压缩的二进制文件),并将 RDB 文件保存到磁盘中。通过 RDB 文件可以将数据还原至生成 RDB 文件的状态。
03
RDB 持久化命令
Redis 提供了两个命令用于生成 RDB 文件,这两个命令分别是 SAVE 和 BGSAVE。
SAVE 命令
SAVE 命令是阻塞式的命令,即在 SAVE 命令执行期间,Redis 服务器进程会被阻塞,直到 SAVE 命令执行完成,也即 RDB 文件创建完毕为止,在此期间,客户端发送至 Redis 服务器的请求会被拒绝。
BGSAVE 命令
BGSAVE 命令是非阻塞式的命令,即执行 BGSAVE 命令时,会 fork 子进程来异步执行 RDB 文件的创建工作,而父进程(即 Redis 服务器进程)可以继续接收客户端的请求并进行相应的处理。但值得一提的是,在 BGSAVE 命令执行期间,并不是所有的客户端请求服务器都会接收处理。为了避免产生竞争条件, SAVE 命以及新的 BGSAVE 命令,在 BGSAVE 命令执行期间都会被拒绝。而出于对性能的考量, BGREWRITEAOF 命令 (用于异步执行 AOF 文件重写) 的执行则会被延迟到 BGSAVE 命令执行完毕之后。
自动持久化
既然 Redis 可以通过 RDB 文件实现持久化,那么一个比较自然的想法就是,让 Redis 自动进行持久化,而不用经常显式地执行 SAVE 或者 BGSAVE 命令。Redis 提供了 save 选项用于配置服务器的自动持久化。配置方式如下:
// 100 秒内至少进行 5 次修改时触发自动 RDB
save 100 5
而由于上文中已经提到的,Redis 的 SAVE 命令是阻塞式的,在执行期间服务器会拒绝客户端的请求,因此,自动持久化背后的机制实际上使用的是对客户端更加友好的 BGSAVE 命令。save 配置可以生效多条。
04
RDB 文件结构剖析
在了解了 Redis 如何 RDB 持久化之后,是时候来看一下 RDB 文件的具体结构了。只有了解了 RDB 文件的结构,才能更清楚 RDB 的持久化过程以及数据恢复过程。
RDB 文件结构如下图所示:
从上图可以看出,Redis 的 RDB 文件结构可以分为如下五个部分:
REDIS 是RDB 文件的标识,在载入 RDB 文件时,可以通过该标识快速判断该文件是否是 RDB 文件。
db_version 是一个长度为 4 个字节的字符串,它对应的整数值是 RDB 文件的版本号。
databases 保存了零个或任意多个数据库,以及数据库中的键值对。这一部分内容是 RDB 文件的主体。由于该部分可以保存多个数据库以及其中的键值对,因此,需要对这一部分内容进行进一步的划分。对于每一个数据库,它内部的结构如下图所示:
从上图可以看出,databases 中的每一个数据库内部结构可以分为如下三个部分:
SELECTDB 是一个长度为 1 个字节的标识,每一个数据库都以 SELECTDB 为起始,不同的数据库之间依靠该标识作为分割。
db_number 是数据库的号码,代表接下来要读的是几号数据库。
key_value_pairs 是 db_number 数据库中所有的键值对。
EOF 是一个长度为 1 个字节的标识,用于表示 databases 内容的结束。
check_sum 是一个长度为 8 个字节的无符号整数,用于保存该 RDB 文件的校验和,依据该校验和来检查和判断 RDB 文件是否正常。
至此,我们已经了解了 RDB 文件的基本结构,Redis 在执行 SAVE 命令或者 BGSAVE 命令的时候,就会按照上述的文件结构,向磁盘中写入相应的 RDB 文件。
05
RDB 持久化的不可靠性
在了解了 Redis 的 RDB 持久化机制后,我们会发现一个问题——RDB 持久化的不可靠问题。这个问题也是面试官经常考察的一点。
因为 RDB 文件的写入并不是实时的,即便是在配置文件中配置了 save 属性,在触发 BGSAVE 命令之前,并不会执行 RDB 文件的写入操作,如果 Redis 服务进程意外退出,此时通过 RDB 文件仅仅能将数据库状态恢复到上一次 RDB 持久化的状态,在此之后所做的改动将会被丢失,这也就是 RDB 持久化不可靠的根源所在。
因此,对于数据丢失并不敏感的情况下,可以采用 RDB 持久化的方式。而对于数据丢失相对比较敏感的情况下,可以采用 Redis 的另外一种持久化的方式——AOF 持久化,在接下来的文章中,将会系统地介绍 AOF 持久化的内容,敬请期待!
综上所述,本文关于 Redis 的 RDB 持久化机制就总结到这里了。
欢迎关注【有理想的菜鸡】公众号,大家一起讨论技术,共同成长!
06
参考资料
《Redis 设计与实现》黄健宏 著
https://github.com/redis/redis
学习 | 工作 | 分享
👆关注“有理想的菜鸡”