前言:redis是内存数据库,它将自己的数据保存在内存中,所以如果不想办法将内存中的数据保存到磁盘里面,那么一旦服务器程序退出,服务器中的数据就会消失不见。
为了解决这个问题,redis提供了RDB持久化功能,这个功能可以将redis内存中的数据保存到磁盘中。RDB持久化既可以手动执行,又可以通过服务器配置来定期执行。RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态。
1、RDB文件的创建和载入
有两个redis命令可以生成RDB文件,一个是SAVE,一个是BGSAVE。
SAVE命令可以直接阻塞服务器进程,知道RDB文件创建完毕。
BGSAVE则是会派生出一个子进程,然后由子进程负责创建RDB文件,父进程继续处理命令请求。
而RDB文件的载入是在服务器启动的时候自动执行的,所以redis并没有用于载入RDB文件的命令,只要redis服务器启动的时候检测到了RDB文件的存在,他就会自动载入RDB文件。
另外, 因为 AOF 文件的保存频率通常要高于 RDB 文件保存的频率, 所以一般来说, AOF 文件中的数据会比 RDB 文件中的数据要新。因此, 如果服务器在启动时, 打开了 AOF 功能, 那么程序优先使用 AOF 文件来还原数据。 只有在 AOF 功能未打开的情况下, Redis 才会使用 RDB 文件来还原数据。
注意:
BGSAVE过程中,如果重新发送SAVE或者BGSAVE命令会被服务器拒绝,因为同时执行rdbSAVE可能会产生竟态。
BGSAVE和BGREWRITEAOF不能同时执行的。如果BGSAVE正在执行,那么客户端发送的BGREWRITEAOF会被延迟到BGSAVE命令执行完毕之后执行。如果BGREWRITEAOF正在执行,那么BGSAVE会被拒绝。其实这两个命令在操作方面并没有什么冲突的大方,都是子进程来执行,不能同时执行只是出于性能的考虑——两个子进程同时大量写入磁盘总感觉不是一个好主意。
2、自动间隔性保存
因为BGSAVE命令不阻塞服务器的主进程,所以redis允许用户在配置文件中通过设置save选项,来每隔一段时间自动执行一次BGSAVE。
save 900 1
save 300 10
save 60 10000
上述条件只需要满足一个即执行BGSAVE。比如900秒之类,对数据库至少对了一次修改。
2.1、保存设置的save条件
struct redisServer {
struct saveparam *saveparams; // 记录了保存条件的数组
long long dirty; // 修改计数器
time_t lastsave;// 上一次执行保存的时间
}
struct saveparam{
time_t seconds; // 秒数
int changes; //修改数
}
dirty计数器记录距离上一次成功执行SAVE或者BGSAVE命令之后,服务器对数据库状态进行了多少次修改。
lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE或BGSAVE命令的时间。
2.2、检查保存条件是否满足
redis的服务器周期性操作函数severCron默认是100ms执行一次(hz设置),该函数其中一项工作就是检查save选项所设置的保存条件是否满足,如果满足,执行BGSAVE指令。
3、RDB文件结构
REDIS:
文件的最开头保存着 REDIS 五个字符,标识着一个 RDB 文件的开始。在读入文件的时候,程序可以通过检查一个文件的前五个字节,来快速地判断该文件是否有可能是 RDB 文件。
RDB-VERSION:
一个四字节长的以字符表示的整数,记录了该文件所使用的 RDB 版本号。因为不同版本的 RDB 文件互不兼容,所以在读入程序时,需要根据版本来选择不同的读入方式。
EOF :
常量,长度为 1 字节, 这个常量标志着 RDB 文件正文内容的结束, 当读入程序遇到这个值的时候, 它知道所有数据库的所有键值对都已经载入完毕了。
check_sum :
是一个 8 字节长的无符号整数, 保存着一个校验和, 这个校验和是程序通过对 REDIS 、 db_version 、 databases 、 EOF 四个部分的内容进行计算得出的。 服务器在载入 RDB 文件时, 会将载入数据所计算出的校验和与 check_sum 所记录的校验和进行对比, 以此来检查 RDB 文件是否有出错或者损坏的情况出现。
3.1、 databases部分
一个 RDB 文件的 databases 部分可以保存任意多个非空数据库。
每个非空数据库的RDB文件都可以保存为 SELECTDB 、 db_number 、 key_value_pairs 三个部分
SELECTDB 常量的长度为 1 字节, 当读入程序遇到这个值的时候, 它知道接下来要读入的将是一个数据库号码。
db_number 保存着一个数据库号码, 根据号码的大小不同, 这个部分的长度可以是 1 字节、 2 字节或者 5 字节。 当程序读入 db_number 部分之后, 服务器会调用 SELECT 命令, 根据读入的数据库号码进行数据库切换, 使得之后读入的键值对可以载入到正确的数据库中。
key_value_pairs 部分保存了数据库中的所有键值对数据, 如果键值对带有过期时间, 那么过期时间也会和键值对保存在一起。 根据键值对的数量、类型、内容、以及是否有过期时间等条件的不同, key_value_pairs 部分的长度也会有所不同。key_value_pairs具体编码。