Redis的持久化机制有两种:RDB持久化和AOF持久化。因为Redis是一个内存数据库,如果没有合适的持久化机制,那么一旦服务器进程退出,服务器中的数据库状态也会消失。本章介绍RDB持久化机制。
RDB持久化
RDB持久化,是Redis可以将数据库状态保存到一个RDB文件中,并可以通过该RDB文件生成RDB文件的时候的数据库状态。RDB文件是一个经过压缩的二进制文件。
生成RDB文件的Redis命令有两个:
- SAVE
- BGSAVE
SAVE命令会阻塞Redis服务器,此期间服务器不能处理任何命令请求;
BGSAVE命令是派生(fork)出一个子进程,然后子进程负责创建RDB文件,服务器继续处理命令请求;
BGSAVE命令执行的服务器状态
BGSAVE期间,服务器处理SAVE、BGSAVE、BGREWRIREAOF三个命令的方式与平时不同。
BGSAVE期间,服务器会直接拒绝客户端发送的SAVE命令,服务器禁止SAVE命令与BGSAVE命令同时进行是为了避免服务器进程和子进程同时执行两个rdbSave调用产生竞争条件。
同理,BGSAVE期间的BGSAVE命令也会直接拒绝。
BGREWRITEAOF和BGSAVE两个命令不能同时执行:
- 如果BGSAVE命令正在执行,客户端发送的BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行;
- 如果BGREWRITEAOF命令正在执行,那么客户端发送的BGSAVE命令会被服务器拒绝;
BGREWRITEAOF和BGSAVE不能同时使用主要是处于性能的考虑,因为两个命令都需要执行大量的磁盘写入操作。
自动间隔性保存
在Redis中,用户可以通过save选项设置多个保存条件,只要其中任意一个条件满足,服务器便执行BGSAVE命令。save 900 1
save 间隔时间(单位为秒) 修改次数
在源码底层的体现是,所有的save选项条件是通过一个saveparam结构保存的。
点击查看代码
struct saveparam {
time_t seconds;
int changes;
};
服务器状态维持了一个dirty计数器和lastsave属性,dirty计数器记录了距离上一次成功执行SAVE命令或者BGSAVE命令之后服务器对数据库状态进行修改的次数。lastsave是一个UNIX时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间。
然后Redis有一个服务器周期函数,用来周期检查是否满足save条件,如果满足则进行BGSAVE。
以上便是Redis自动进行间隔性数据保存的原理。
RDB文件结构
RDB文件分为五个部分:REDIS、db_version、databases、EOF、check_sum。
REDIS
REDIS部分的作用:标识该文件是一个RDB文件。
保存着“REDIS”五个字符。
db_version
db_version部分的作用:标识RDB文件的版本号。
databases
databases部分的作用:存储数据库信息。
databases部分包含零个或任意多个数据库,以及各个数据库的键值对。
若服务器的数据库状态为空,那么该部分也为空。否则存储各个数据库的信息。
每一个非空数据库在RDB文件中可以保存为SELECTDB、db_number、key_value_pairs三个部分。
SELECTDB长度为1字节,表示接下来读入的会是一个数据库号码。
db_number保存着一个数据库号码,根据号码不同,服务器使用SELECT命令进行数据库切换。
key_value_pairs部分保存数据库中所有键值对数据,包括如果有过期时间便带有过期时间。
key_value_pairs
key_value_pairs分为三个部分:TYPE,key,value。
TYPE记录的是value的类型,有以下几种常量:
- REDIS_RDB_TYPE_STRING
- REDIS_RDB_TYPE_LIST
- REDIS_RDB_TYPE_SET
- REDIS_RDB_TYPE_ZSET
- REDIS_RDB_TYPE_HASH
- REDIS_RDB_TYPE_LIST_ZIPLIST
- REDIS_RDB_TYPE_SET_INTSET
- REDIS_RDB_TYPE_ZSET_ZIPLIST
- REDIS_RDB_TYPE_HASH_ZIPLIST
带有过期时间的键值则在三个部分的前面在加上两个部分:EXPIRETIME_MS,ms。
前面的为标记是存在过期时间的,后面的为过期时间。
value的编码
value的编码中的编码与Redis底层数据类型有关,这里我们先写value的编码。
字符串对象
字符串对象是TYPE为REDIS_RDB_TYPE_STRING的对象。
字符串对象存在可以存储INT值的编码,如果为此种编码,那么在文件中的存储便是先存储编码,再存储值。ENCODING | integer
字符串对象如果是存储字符串的编码,有压缩和不压缩两种方法保存字符串。
若字符串长度小于20字节,那么便会原样保存;若字符串长读大于20字节,那么便压缩之后再保存。
如果是无压缩的字符串,存储在RDB文件中便是先存储字符串长度,再存储值。len | string
如果是压缩的字符串,那么存储在RDB中的格式如下:REDIS_RDB_ENC_LZF(标识已被LZF算法压缩) | compressed_len(压缩后长度) | origin_len(字符串原长度) | compressed_string(压缩之后的字符串)
列表对象
列表对象的RDB文件存储格式如下:list_length(列表长度) | item1 | item2 | itemN
集合对象
与列表对象类型,只是TYPE会不同。set_size(列表长度) | elem1 | elem2 | elemN
哈希表对象
哈希表的RDB文件存储格式如下:hash_size|key_value_pair1|……
有序集合对象
与集合对象只是TYPE不同。
INTSET对象
TYPE为REDIS_RDB_TYPE_SET_INTSET,然后将其转换为字符串对象。读取RDB文件时候,会将字符串对象转换为原来的INTSET对象。
ZIPLIST编码的列表、哈希表或者有序集合
value为一个压缩列表对象,读取时只是根据TYPE不同进行逆过程。
总结
RDB是Redis两种持久化方法之一。
RDB优点:
- 生成多个数据文件,每一个数据文件代表某一个时刻的redis的数据,适合冷备份。
- RDB对redis对外提供读写服务影响小,可以让redis保持高性能。
- 相较于AOF持久化机制而言更快。
RDB缺点:
- RDB是一个自动间隔性保存的机制,所以丢失数据量可能会比AOF多。
- 如果RDB文件过大,会导致对客户端提供的服务暂停一段时间。