Redis是内存数据库,它将自己的数据保存在内存里面。那么一旦服务器进程退出,服务器里的数据也会消失不见。为了解决这个问题,Redis提供了RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里面,避免数据意外丢失。
RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中。执行之后会生成一个经过压缩的二进制文件,通过该文件就可以还原生成RDB文件时数据库状态。
RDB文件的保存和还原.png

RDB文件的创建与载入

有两个Redis命令可以用于生成RDB文件,一个是SAVE,另一个是BGSAVE。


SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求:

redis> SAVE       //等待直到RDB文件创建完毕
OK

和SAVE命令直接阻塞服务器进程的做法不同,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求:

redis> BGSAVE     //派生子进程,并由子进程创建RDB文件
Background saving started

和创建RDB文件不同,Redis没有载入RDB文件的相关命令。而是只要服务器在启动时检测到RDB文件的存在,它就会自动载入RDB文件(DB loaded from disk)。

$ redis-server
[7379] 30 Aug 21:07:01.270 # Server started, Redis version 2.9.11
[7379] 30 Aug 21:07:01.289 * DB loaded from disk: 0.018 seconds
[7379] 30 Aug 21:07:01.289 * The server is now ready to accept connections on port 6379

另外值得一提的是,因为AOF文件的更新频率通常比RDB文件的更新频率高,所以:

  • 如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库状态。
  • 只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态。

服务器载入文件判断流程.png

SAVE命令执行时服务器状态

前面提到过,当SAVE命令执行时,Redis服务器会被阻塞,所以当SAVE命令正在执行时,客户端发送的所有命令请求都会被拒绝。只有在服务器执行完SAVE命令、重新开始接受命令请求之后,客户端发送的命令才会被处理。

BGSAVE命令执行时服务器状态

首先,在BGSAVE命令执行期间,客户端发送的SAVE命令会被服务器拒绝,服务器禁止SAVE命令和BGSAVE命令同时执行是为了避免父进程(服务器进程)和子进程同时执行两个rdbSave调用,防止产生竞争条件。
其次,在BGSAVE命令执行期间,客户端发送的BGSAVE命令会被服务器拒绝,因为同时执行两个BGSAVE命令也会产生竞争条件。
最后,BGREWRITEAOF(异步执行一个AOF文件重写操作)和BGSAVE两个命令不能同时执行。

RDB文件载入时服务器状态

服务器在载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。


自动间隔保存

Redis允许用户通过配置服务器的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。


举个例子,我们向服务器提供以下配置:

save 900 1
save 300 10
save 60 10000

那么只要满足以下三个条件中的任意一个,BGSAVE命令就会被执行:

  • 服务器在900秒之内,对数据库进行了至少1次修改。
  • 服务器在300秒之内,对数据库进行了至少10次修改。
  • 服务器在60秒之内,对数据库进行了至少10000次修改。

设置保存时间

关于保存时间的属性,保存在redisServer结构体里:

struct saveparam {
    // 秒数
    time_t seconds;
    // 修改数
    int changes;
};

struct redisServer {
    // ...
    // 记录了保存条件的数组
    struct saveparam *saveparams;
    // ...
};

以刚才的配置举例,此时的服务器自动保存状态应该是:
服务器状态中的保存时间.png
除了saveparams数组之外,服务器状态还维持着一个dirty计数器,以及一个lastsave属性:

  • dirty计数器记录距离上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(包括写入、删除、更新等操作)。
  • lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间。
struct redisServer {
    // ...
    // 修改计数器
    long long dirty;
    // 上一次执行保存的时间
    time_t lastsave;
    // ...
};

如果我们向服务器添加三个元素,且执行此操作保存的时间是1378270800:

redis> SADD database Redis MongoDB MariaDB
(integer) 3

服务器状态示例.png

检查保存条件是否满足

Redis的服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,该函数用于对正在运行的服务器进行维护,它的其中一项工作就是检查save选项所设置的保存条件是否已经满足,如果满足的话,就执行BGSAVE命令。


举个例子:如果当前服务器状态如下所示:
执行BGSAVE前服务器状态.png
那么当时间来到1378271101,也即是1378270800的301秒之后,服务器将自动执行一次BGSAVE命令,因为saveparams数组的第二个保存条件——300秒之内有至少10次修改已经被满足。


假设BGSAVE在执行5秒之后完成,那么上面的服务器状态将会更新,其中dirty计数器已经被重置为0,而lastsave属性也被更新为1378271106。
执行BGSAVE后服务器状态.png

RDB文件结构

RDB的文件结构挺复杂的,但是用一张图就可以说请:
RDB文件结构.png
其中TYPE可以是:

// 字符串类型
REDIS_RDB_TYPE_STRING
// 列表类型
REDIS_RDB_TYPE_LIST
// 集合类型
REDIS_RDB_TYPE_SET
// 有序集合类型
REDIS_RDB_TYPE_ZSET
// 哈希类型
REDIS_RDB_TYPE_HASH
// 使用压缩列表实现的列表类型
REDIS_RDB_TYPE_LIST_ZIPLIST
// 使用整数集和实现的集合类型
REDIS_RDB_TYPE_SET_INTSET
// 使用压缩列表实现的有序集合类型
REDIS_RDB_TYPE_ZSET_ZIPLIST
// 使用压缩列表实现的哈希类型
REDIS_RDB_TYPE_HASH_ZIPLIST

在上图中value储存的是字符串对象的INT_8类型整数,所以TYPE就是REDIS_RDB_TYPE_STRING。


value还有一些其他类型的对象:
字符串和压缩字符串类型.png
列表对象.png
集合对象.png
有序集合对象.png
哈希表对象.png

Last modification:June 10th, 2020 at 08:42 am
如果觉得我的文章对你有用,请随意赞赏