一、背景
redis是一个内存数据库,把数据库的状态放在内存里面,但是如果服务器退出,那么内存的数据就会被清空。 因此,redis提供了RDB持久化,把数据保存在磁盘里面,防止数据的丢失。
注意:数据恢复的时候,如果开启了AOF持久化,那么会优先使用AOF来还原数据
二、文件的创建
保存 RDB 文件的方法:SAVE 以及 BGSAVE
区别:
SAVE会阻塞服务器的进程,新的操作请求会被操作系统内核的网络缓冲区缓存起来,它们的执行会被延迟,直到SAVE操作完成,Redis 主线程恢复工作。BGSAVE会创建一个子进程,创建RDB文件,当完成以后向父进程发送信号。父进程则继续处理命令请求,并通过轮询方式等待子进程的信号
Q:那么BGSAVE开启了一个子进程进行数据保存的工作,那么如果我尝试再次执行SAVA,BGSAVE会怎么样?
A:会被拒绝,服务器不允许父子进程,或者多个进程对文件进行操作(出于性能考虑以及防止竞态条件的产生)
三、自动保存
save time cnt:服务器在time秒以内,对数据库进行了至少cnt次修改,那么就会执行BGSAVE命令。比如 save 300 10:服务器在300s以内,对数据库进行了至少10次的修改。
可以设置多个保存条件,只要有一个生效就行,因此其底层是保存这些状态用的是一个数组,那么redis通过遍历整个数组来判断是否会出现问题。
redis里面有一个结构体:
struct redisServer{
//...
// 记录当前修改的次数
int dirty;
// 记录上次执行保存的时间
time_t lastsave;
// 记录定时保存的参数(数组结构)
struct saveparam* saveparams;
//...
}
struct saveparam{
time_t seconds;
int changes
}
保存完成以后,修改dirty以及lastsave的内容
Q:那么保存状态是什么样的?比如我save 900 1:900s以内修改一次数据就保存?那岂不是每次操作都保存一次?
A:对时间的判断是 time_now() - lastsave>900 && dirty>=1才会保存,也就是超过900s才行。
四、RDB文件结构
- REDIS:就是REDIS这五个字符,表示这是一个REDIS文件。
- db_version:记录当前的RDB的版本,会向后兼容。
- database:记录整个数据库(可以有多个数据库)的kv数据
- EOF:一个字节,标志着内容的结束
- check_sum:是一个校验和,对前面四个数据段的校验(八字节)
4.1 database 部分
SELECTDB是一字节,标识着下一个数据是数据库号码。读取到数据库号码以后,就会调用SELECT命令,根据读入的数据库号码进行切换。
key_value_pairs:保存了数据库中所有键值对的数据,如果其有过期时间,那么过期时间也会随着一起进行保存。
4.2 kV部分
不带过期时间的KV
带有过期时间的KV
- TYPE记录的是value的类型(一字节标识),其key一直都是string对象。
- EXPIRETIME_MS:也是一个标识(1字节),标识下一个读入的是过期时间。
- ms:一个8字节长的有符号整数,UNIX时间戳
4.3 value的编码
1. 字符串
字符串对象涉及到两个:存的是整型数据以及真字符串
-
如果存的是整数、
那么其value里面会有一个字节进行标识,比如这个标记字节的高2位通常是
11,后6位指示具体是INT8、INT16还是INT32 -
如果存的是字符串
- 如果是LZF 压缩的字符串,那么其结构如下:
- 它的高2位是
11,后6位指示这是 LZF 压缩。
- 如果是普通的字符串,那么:
- 如果第一个字节的头2位是
00,则长度在后6位 (长度 0-63)。 - 如果第一个字节的头2位是
01,则长度在后6位 + 下一个字节 (长度 64-16383)。 - 如果第一个字节的头2位是
10,则长度在接下来的4个字节 (长度可达 2^32−1)。
- 如果是LZF 压缩的字符串,那么其结构如下:
2. 集合类型
Redis 中的集合类型,如列表 (List)、集合 (Set)、哈希 (Hash) 和有序集合 (Sorted Set),在 RDB 文件中存储时,其结构具有相似的模式:首先记录集合内元素的数量,然后依次存储各个元素。
通用结构模式:
+--------------------------+-----------------+-----------------+-----+-----------------+
| 元素数量 (Collection Size) | 第一个元素 (Item1) | 第二个元素 (Item2) | ... | 第 N 个元素 (ItemN) |
+--------------------------+-----------------+-----------------+-----+-----------------+
以哈希为例,里面的数据格式为:
Hash_Value_RDB: +-------------+----------------+---------------+----------------+-----------------+-------------------+-----------------+
| 元素数量(2) | "name"长度(4) | "name"数据 | "redis"长度(5) | "redis"数据 | "version"长度(7) | "version"数据 | ... (接"7")
| (1 byte) | (1 byte, 0x04) | 'n''a''m''e' | (1 byte, 0x05) | 'r''e''d''i''s' | (1 byte, 0x07) | 'v''e''r''s'...' |
+-------------+----------------+---------------+----------------+-----------------+-------------------+-----------------+