Redis设计与实现 第 10 章 RDB 持久化
第 10 章 RDB 持久化
数据库状态:服务器中的非空数据库以及它们的键值对统称为数据库状态
Redis 提供 RDB 持久化功能,将内存中的数据库状态保存到磁盘中,避免数据意外丢失
RDB 文件是一个经过压缩的二进制文件,还可以通过该文件还原生成 RDB 文件时的数据库状态
10.1 RDB 文件的创建与载入
SAVE、BGSAVE命令生成 RDB 文件
SAVE 命令会阻塞 Redis 服务器进程,直到文件创建完毕,阻塞期间无法处理任何请求
BGSAVE 命令会派生子进程,由子进程负责创建 RDB 文件,服务器父进程继续处理命令请求
创建 RDB 文件的实际工作由 rdb.c/rdbSave 完成,两个命令会以不同的方式调用此函数
RDB 文件的载入工作在服务器启动时自动执行的,只要检测到 RDB 文件存在,自动载入 RDB 文件
AOF文件更新频率通常比 RDB 文件的更新频率高,所以:
- 如果服务器开启了 AOF 持久化功能,服务器会优先使用 AOF 文件来还原数据库状态
- 只有在 AOF 持久化功能关闭才会使用 RDB 文件来还原状态
10.1.1 SAVE 命令执行时的服务器状态
阻塞,只有命令执行完成后客户端的命令才会被处理
10.1.2 BGSAVE 命令执行时的服务器状态
服务器可以继续处理命令请求,但有些区别
- SAVE 命令被拒绝
- BGSAVE 命令也被拒绝
- BGREWRITEAOF 不能同时执行
- BGSAVE 在执行,被延迟到前者完成后
- 正在执行,BGSAVE 被拒绝
- 性能方面考虑,两个子进程同时执行大量写入操作
10.1.3 RDB 文件载入时服务器的状态
阻塞
10.2 自动间隔性保存
设置服务器配置 save 选项,让服务器妹隔一段时间自动执行一次BGSAVE 选项
save 900 1
save 300 10
save 60 10000
在 900 s 内,对数据库进行了至少一次修改
同理
三个条件满足一个,BGSAVE 命令就会执行
10.2.1 设置保存条件
save 900 1
save 300 10
save 60 10000
以上为默认设置,如果没有指定配置参数或者传入启动参数方式
服务器会根据 save 选项设置 redisServer.saveparams 属性
默认情况如下:
10.2.2 dirty 计数器和 lastsave 属性
- dirty计数器
- 记录距离上一次执行 SAVE 命令或者 BGSAVE 命令之后服务器对数据库进行了多少次修改
- lastsave
- UNIX 时间戳
- 记录了服务器上一次成功执行 SAVE 命令或 BGSAVE 命令的时间
- UNIX 时间戳
服务器成功执行一次数据库修改命令之后就对 dirty 计数器进行更新
10.2.3 检查保存条件是否满足
serverCron 默认每隔 100 ms 执行一次,对运行的服务器维护,其中一项工作就是检查设置的保存条件是否满足,是则执行 BGSAVE 命令
10.3 RDB 文件结构
完整的 RDB 文件结构如下:
常量:全大写单词
变量和数据:全小写单词
- REDIS:5 字节,保存 “REDIS” 5 个字符,方便程序快速分辨为 RDB 文件
RDB 为二进制数据,“REDIS” 保存的不是 c 字符串
-
db_version:4 字节,为字符串表示的常数, REB 版本号
-
database
-
零个或多个数据库,以及数据库中的键值对数据
-
如果数据库状态为空,此处也为空,长度 0 字节
-
数据库状态非空,根据情况不同,长度不同
-
-
EOF:常量,1 字节,标志 RDB 文件正文内容结束,即所有数据库所有键值对载入完成
-
check_num:8 字节无符号整数,校验和,通过前四个部分计算而来,载入时计算是否与之相同验证文件是否损坏
10.3.1 database 部分
database 可以保存任意多个非空数据库
假设 0 号、3 号数据库非空, RDB 文件如图,database 代表 0 号数据库中的所有键值对
database 0 又可以再细分
-
SELECTDB:1 字节,表明接下来读入的是数据库号码
-
db_number:数据库号码,可以为1 3 5 字节
-
key_value_pairs:数据库中的所有键值对数据,如果有过期时间也会保存在一起
10.3.2 key_value_pairs 部分
不带过期时间的键值对
TYPE | KEY | VALUE |
---|
- TYPE:1 字节
- value:字符串对象,同上襦的 String 类型
带过期时间的键值对
EXPIRETIME_MS | MS | TYPE | KEY | VALUE |
---|
- EXPIRETIME_MS:1 字节,告知程序即将读入的是毫秒为单位的过期时间
- ms:8 字节的带符号整数,毫秒为单位为 UNIX 时间戳
10.3.3 value 编码
1.字符串对象:REDIS_RDB_TYPE_STRING
value 保存的为字符串对象,编码有 REDIS_ENCODING_INT 或者 REDIS_ENCODING_RAW
-
REDIS_ENCODING_INT
-
长度不超过 32 位的整数
-
ENCODING 可以是 REDIS_RDB_ENC_INT8、REDIS_RDB_ENC_INT16、REDIS_RDB_ENC_INT32 代表使用几位来保存整数值 integer
-
-
REDIS_ENCODING_RAW
-
字符串对象
-
压缩与不压缩保存
-
小于等于 20 字节,原样保存
-
大于 20 字节,压缩之后再保存
-
在服务器打开 RDB 文件压缩功能进行的
-
否则总是无压缩保存
-
-
无压缩
-
压缩
- REDIS_RDB_ENC_LZF:常量,表示字符串被 LZF 算法压缩了,程序会对后面三个字段使用解压缩
- compressed_len:压缩之后的长度
- origin_len:压缩之前的长度
- compressed_string:压缩之后的字符串
-
2.列表对象:REDIS_RDB_TYPE_LIST
value 保存的是 REDIS_ENCODING_LINKEDLIST 编码的列表对象
-
list_length:列表长度,列表保存了多少个项
-
item:列表的列表项,每个列表项都是字符串对象
- 包含长度与实际值
3.集合对象:REDIS_RDB_TYPE_SET
REDIS_ENCODING_HT
-
set_size:集合大小
-
elem:字符串对象
- 包含长度与实际值
4.哈希表对象:REDIS_RDB_TYPE_HASH
REDIS_ENCODING_HT
键与值相邻排列
5.有序集合对象:REDIS_RDB_TYPE_ZSET
REDIS_ENCODING_SKIPLIST
成员与分值紧挨,分值为 double 浮点数,在 double、字符串中互相转换
6.INTSET 编码集合:REDIS_RDB_TYPE_INTSET
整数集合与字符串互相转换
7.ZIPLIST 编码的列表、哈希表或者有序集合
REDIS_RDB_TYPE_LIST_ZIPLIST
REDIS_RDB_TYPE_HASH_ZIPLIST
REDIS_RDB_TYPE_ZSET_ZIPLIST
压缩列表
-
压缩列表转换字符串
-
字符串保存到 RDB
-
字符串转换压缩列表
-
根据 TYPE 设置类型
- REDIS_RDB_TYPE_LIST_ZIPLIST:列表
- REDIS_RDB_TYPE_HASH_ZIPLIST:哈希表
- REDIS_RDB_TYPE_ZSET_ZIPLIST:有序集合