[Redis][10][RDB持久化]

第 10 章 RDB持久化

Redis是一个键值对数据库服务器,服务器中通常包含着任意个非空数据库,而每个非空数据库中又可以包含任意个键值对,为了方便起见,我们将服务器中的非空数据库以及它的键值对统称为数据库状态

一个数据库状态如下图所示

Redis是内存数据库,它将自己的数据库状态储存在内存里面,一旦服务器进程退出,服务器中的数据库状态也会消失不见。为了解决这个问题, Redis提供了RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里面,避免数据意外丢失

10.1 RDB文件的创建与载入

RDB文件的创建命令有两个

  • SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
  • BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求

Redis并没有专门用于载入RDB文件的命令,只要Redis服务器在启动时检测到RDB文件存在,它就会自动载入RDB文件。

值得一提的是,因为AOF文件的更新频率更快,所以通常优先使用AOF文件来还原数据库状态,当没有开启AOF功能时,才使用RDB文件,判断流程如下

此外

  • 当运行SAVE时,会阻塞所有命令
  • 当运行BGSAVE时,SAVEBGSAVE命令会被服务器拒绝BGREWRITEAOF命令会被服务器阻塞,直到BGSAVE完成;其他命令则由父进程正常处理
  • 载入RDB文件时,会阻塞所有命令

10.2 自动间隔性保存

Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。
用户可以通过save选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行BGSAVE命令。

例如条件如下

1
2
save 900 1
save 300 10

这两个条件的解释是

  • 若服务器在900秒内,对数据库进行了1次修改,则执行BGSAVE
  • 若服务器在300秒内,对数据库进行了10次修改,则执行BGSAVE

10.2.1 设置保存条件

运行时,通过配置文件或者传入参数设置的保存条件会被加载到redisServer结构的saveParams属性中,结构如下图

此外,服务器状态还维持着一个dirty计数器,以及一个lastsave属性

  • dirty计数器记录距离上一次成功保存之后,服务器对数据库状态进行了多少次修改
  • lastsave属性是一个UNIX时间戳,记录了服务器上一次成功保存的时间

10.2.2 检查保存条件是否满足

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

下面的伪代码展示了serverCron函数检查保存条件的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def serverCron():
# ...

# 遍历所有保存条件
for saveparam in server.saveparams:

# 计算距离上次执行保存操作有多少秒
save_interval = unixtime_now() - server.lastsave

# 如果数据库状态的修改次数超过条件所设置的次数
# 并且距离上次保存的时间超过了条件所设置的时间
# 就执行保存操作
if server.dirty >= saveparam.changes and save_interval > saveparam.seconds:
BGSAVE()

# ...

10.3 RDB文件结构

一个完整的RDB文件包含下图所示的部分

  • 最开头是五个字符"REDIS",占5字节,用来标记这个文件是RDB文件
  • db_version长度为4字节,记录这个文件的版本号
  • databases部分包含了某个时间的数据库状态,也就是说它是数据部分
  • EOF是终止符,表示databases部分的结束,占1字节
  • check_sum是一个校验和,占8字节

10.3.1 databases部分

一个RDB文件的databases部分包含多个非空数据库,按顺序存储

每个非空数据库被保存为三部分

  • SELECTDB占1字节,当程序读到这个字节,它就直到下面要读的是一个数据库号码
  • db_numbers保存着一个数据库号码,载入程序读取后,会调用SELECT命令切换到指定数据库,然后再加载键值对
  • key_value_pairs实际保存了数据库中的所有键值对数据,如果键值对有过期时间,也会保存在一起

一个完整的RDB结构如下所示,假设0号和3号数据库是非空数据库

10.3.2 key_value_pairs部分

每个key_value_pair结构如下

  • EXPIRETIME_MS表示接下来要读的是一个时间戳
  • ms保存了过期时间,占8字节
  • TYPE保存了键值对的类型,对于不同的类型,RDB采用了不同的存储方式
  • key实际保存键对象
  • value实际保存了值对象

例如下图

10.3.3 value的编码

不同类型的值对象在RDB文件中的保存结构不同,这里不展开介绍,但是对于intset和实际编码为ziplist的对象,都会被转化为字符串对象再存储。

10.3.4 分析RDB文件

可以使用Redis自带的RDB文件检查工具redis-check-dump来检查RDB文件