1,前言
有关RDB
文件生成相关的可以看:RDB持久化 本文涉及到的各种对象:Redis对象与数据结构
2,RDB文件结构
一个完整的RDB
文件的示意图如下:
【为了方便区分变量、数据、常量,全文中用全大写单词标示常量,用全小写单词标示变量和数据】
-
RDB
文件开头是REDIS
部分,这个部分长5个字节,保存着REDIS
五个字符。通过这五个字符,程序可以在载入文件时,快速判断是否是RDB
文件 -
db_version
长度为4个字节,它的值是一个字符串表示的整数,记录了RDB
文件的版本号,比如0006表示RDB
文件的版本为第六版 -
databases
部分包含着零个或者任意个数据库,以及数据库中的键值对数据 -
EOF
常量的长度为一个字节,这个常量标示着RDB
文件正文内容的结束 check_sum
是一个8字节长的无符号整数,保存着一个校验和,这个校验和是程序通过对REDIS、db_version、databases、EOF
四个部分的内容进行计算得出的。服务器在载入时,会将载入数据计算得出的校验和与check_sum
进行比较,以此判断是否出现出错或者损坏
示例:
2.1,databases部分
一个RDB
文件的databases
部分可以保存任意多个非空数据库,假设有一个服务器,其0号与3号数据库非空,那么其RDB文件为:
而每个非空数据库在RDB
中又是按以下结构进行保存的:
-
SELECTDB
常量的长度是一个字节,当读入程序遇到这个值的时候,它知道接下来要读入的将是一个数据库号码 -
db_number
保存着一个数据库号码,根据号码的大小不同,这个部分的长度可以是1字节、2字节或者5字节。当读入db_number
后,服务器会调用SELECT命令,根据读入的数据库号进行数据库切换,使得之后读入的键值对可以载入到正确的数据库中 -
key_value_pairs
部分保存了数据库中的所有键值对数据,如果键值对带有过期时间,那么过期时间也会和键值对保存在一起
2.2,key_value_pairs部分
RDB
文件中的每个key_value_pairs
部分都保存了一个或以上数量的键值对,如果键值对带有过期时间的话,那么键值对的过期时间也会被保存在内
如果不带有过期时间的话,结构会是这样的:
带有过期时间的话,结构会是这样的:
各部分的意义如下:
TYPE
:记录了value
的类型,长度为一个字节,当服务器读入RDB
文件中的键值对时,会根据value
的值决定如何读入和解释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
-
key
:总是一个字符串对象,它的编码方式和REDIS_RDB_TYPE_STRING
类型的value
一样 -
value
:根据TYPE
类型的不同,以及保存内容长度的不同,保存的value
的结构和长度也会有所不同 -
EXPIRETIME_MS
:常量的长度为一个字节,它告诉读入程序,接下来要读入的将是一个以毫秒为单位的过期时间 -
ms
:一个8字节长的带符号证书,记录着一个以毫秒为单位的UNIX
时间戳,这个时间戳就是这个键值对的过期时间
例如:
2.3,value编码
RDB
文件中的每个value
部分都保存了一个值对象,每个值对象的类型都由与之对应的TYPE
记录,根据类型不同,value
的长度与结构也都不同
2.3.1,字符串对象
如果TYPE
的值是REDIS_RDB_TYPE_STRING
,那么value
保存的就是一个字符串对象,字符串对象的编码可以为:
REDIS_ENCODING_INT
REDIS_ENCODING_RAW
REDIS_ENCODING_INT
如果字符串对象的编码为:REDIS_ENCODING_INT
,那么说明保存的是长度不超过32位的整数,这种编码的对象将会按照下图的结构进行存储(假设字符串对象中保存的是用8位来保存的整数123):
REDIS_ENCODING_RAW
如果字符串编码为:REDIS_ENCODING_RAW
,那么说明这个对象所保存的是一个字符串值,根据字符串长度的不同,有压缩和不压缩两种方法进行保存:
- 如果字符串的长度小于等于20字节,字符串会被原样保存
- 如果字符串的长度大于20字节,那么这个字符串会被压缩后再进行保存
【值得一提的是:只有当服务器打开了RDB文件压缩功能时才会出现上述的情况;否则RDB
程序总会以无压缩的方式保存字符串值】
如果是以不压缩的方式进行保存的话:
-
string
:保存了字符串本身 -
len
:保存了字符串值的长度
如果是以压缩的方式进行保存的话,RDB
程序会用以下的结构进行保存:
-
REDIS_RDB_ENC_LZF
:表示字符串已经被LZF算法压缩过;读入程序碰到这个常量后,会根据之后的compressed_len、origin_len、compressed_string
三部分,对字符串进行解析 -
compressed_len
:字符串压缩过后的长度 -
origin_len
:字符串原来的长度 -
compressed_string
:被压缩后的字符串
2.3.2,列表对象
如果TYPE
的值是REDIS_RDB_TYPE_LIST
,那么value
保存的就是一个REDS_ENCODING_LINKEDLIST
编码的列表对象,RDB
文件保存这种对象的结构如下:
list_length
记录了列表的长度,代表列表保存了多少个项,读入程序可以根据这个知道应该读入多少个列表项。以一个包含三个元素的列表为例:
- 第一个数字3是列表的长度,之后跟着的是三个列表项
- 第一个列表项的长度为5,内容为字符串“hello”
- 第二个列表项的长度为5,内容为字符串“world”
- 第三个列表项的长度为1,内容为字符串“!”
2.3.3,集合对象
如果TYPE
的值是REDIS_RDB_TYPE_SET
,那么value
保存的就是一个REDIS_ENCODING_HT
编码的集合对象,RDB
文件保存这种对象的结构如下:
-
set_size
:集合的大小,记录了集合保存多少个元素 -
elemX
:集合项,因为每个集合元素都是字符串对象,所以程序会以处理字符串对象的方式来保存和读入集合元素
例如有一个包含四个元素的集合:
- 第一个数字4,表示有四个元素
- 第一个元素的长度为5,存储的内容是:”apple“
- 第二个元素的长度为6,存储的内容是:”banana“
- 第三个元素的长度为3,存储的内容是:”cat“
- 第四个元素的长度为3,存储的内容是:”dog“
2.3.4,有序集合对象
如果TYPE
的值为REDIS_RDB_TYPE_ZSET
,那么value保存的就是一个REDIS_ENCODING_SKIPLIST
编码的有序集合对象,RDB
文件保存这种对象的结构如下所示:
-
sorted_set_size
:有序集合的大小,表示这个有序集合保存了多少元素 -
elementX
:有序集合中的元素,每个元素又分为成员(member
)与分值(score
),成员是一个字符串对象,分值则是一个double
类型的浮点数。程序在存储分值时,会将其转为字符串对象,再进行保存。
再细分下,能得到下图:
例如有一个带有两个元素的有序集合:
- 第一个数字2表示有序集合中保存了两个元素
- 第一个集合项的成员长度为2,内容为:”pi“,分值被转换成字符串之后变成了长度为4的字符串:”3.14“
- 第二个集合项的成员长度为1,内容为:”e“,分值被转换成字符串之后变成了长度为3的字符串:”2.7“
2.3.5,哈希对象
如果TYPE
的值为REDIS_RDB_TYPE_HASH
,那么value
保存的就是一个REDIS_ENCODING_HT
编码的集合对象,RDB
保存这种对象的结构如下图所示:
-
hash_size
:哈希表的大小,表示哈希表保存了多少键值对 -
key_value_pair X
:代表哈希表中的键值对,键值对中的键和值都是字符串对象,所以程序会以处理字符串对象的方式来保存和读入键值对
结构中的每个键值对都以键紧挨着值的方式排列在一起,所以可以细分为:
假设有一个保存着两个键值对的哈希表:
- 第一个数字2,记录了哈希表的键值对数量
- 第一个键值对的键长度为1,保存的字符串内容为:“a”;值是长度为5的字符串“apple”
- 第二个键值对的键长度为1,保存的字符串内容为:“b”;值是长度为6的字符串“banana”
2.3.6,INSET编码的集合
如果TYPE
的值为REDIS_RDB_TYPE_SET_INTSET
,那么value
保存的就是一个整数集合对象,RDB
文件保存这种对象的方法是,先将整数集合转换为字符串对象,然后将这个字符串对象保存到RDB
文件中。
在读入RDB
文件的过程中,如果遇到由整数集合转换而得来的字符串对象,那么程序会根据TYPE
值,先读入字符串对象,再将其转换为原来的整数集合对象。
2.3.7,ZIPLIST编码的列表、哈希表或者有序集合
如果TYPE
的值是以下几种之一:
REDIS_RDB_TYPE_LIST_ZIPLIST
REDIS_RDB_TYPE_HASH_ZIPLIST
REDIS_RDB_TYPE_ZSET_ZIPLIST
那么value
保存的就是一个压缩列表,RDB
保存着类对象的方法是:
- 将压缩列表转换为一个字符串对象
- 将转换所得的字符串对象保存到
RDB
文件中
在读入RDB
文件时,如果遇到这种情况产生的字符串对象时,程序会根据TYPE
值的指示,执行以下操作:
- 读入字符串对象,将其转换为原来的压缩列表对象
- 根据
TYPE
的值,设置压缩列表对象的类型:
- 如果
TYPE
的值为:REDIS_RDB_TYPE_LIST_ZIPLIST
,那么压缩列表对象的类型为列表 - 如果
TYPE
的值为:REDIS_RDB_TYPE_HASH_ZIPLIST
,那么压缩列表对象的类型为哈希表 - 如果
TYPE
的值为:REDIS_RDB_TYPE_ZSET_ZIPLIST
,那么压缩列表对象的类型为有序集合