第 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时,SAVE和BGSAVE命令会被服务器拒绝;BGREWRITEAOF命令会被服务器阻塞,直到BGSAVE完成;其他命令则由父进程正常处理
- 当载入RDB文件时,会阻塞所有命令
10.2 自动间隔性保存
Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。
用户可以通过save选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行BGSAVE命令。
例如条件如下
1 | save 900 1 |
这两个条件的解释是
- 若服务器在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 | def serverCron(): |
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文件