赞
踩
Redis rdb是Redis快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据,另外搭建从库或者重建从库也需要拉取主库生成的rdb快照文件。rdb不仅应用于备份恢复和搭建从库,也广泛应用于离线key分析,例如:查找BigKey、HotKey、ColdKye或者Key的访问频率等。
我们可以通过输入info Persistence来查看Redis服务端记录的相关持久化状态的信息。
127.0.0.1:8888> info Persistence
#Persistence
loading:0 #是否正在保存rdb文件
rdb_changes_since_last_save:356857684 #最后一次保存rdb文件之后改变的key的个数
rdb_bgsave_in_progress:0 #后台是否正在保存rdb文件
rdb_last_save_time:1640573408 #最后一次保存rdb文件的具体时间
rdb_last_bgsave_status:ok #最后一次保存rdb文件的状态,成功或者失败
rdb_last_bgsave_time_sec:0 #最后一次保存rdb文件消耗的时间
rdb_current_bgsave_time_sec:-1 #当前保存rdb文件所用的时间,"-1"表示后台没有正在rdb文件
rdb_last_cow_size:1064960 #最后一次保存rdb文件的任务消耗的内存
.....
其实Redis生成rdb文件有时候是很重的操作,如果使用不当将会造成严重的生产故障。从生成rdb是否阻塞主线程来看,共有两种方式:阻塞式和非阻塞式。阻塞式:使用Redis主线程完成rdb文件的写入;非阻塞式:用Redis主线程fork的子线程完成rdb文件的写入,主线程只需要记录统计状态和指标。那么触发生成rdb文件的条件有哪些呢?请看下图。
![在这里插入图片描述]
Rdb文件是一个紧凑的二进制文件,整体上可分为三个区域:头信息区、数据区、尾信息区。
下图为使用系统命令od输出的Redis6.2.6的rdb文件内容,第一和第三行是用十六进制显示的 dump.rdb 文件的字节内容,这里每两个十六进制数对应了一个字节,而第二和第四行是 od 命令生成的每个字节所对应的 ASCII 字符,由于ASCII 字符只定义了128个字符,所以部分字符是无法识别的,例如0xfa、0xfe、0xc0等。
注意:rdb文件内容是非常紧凑的,不存在空格、换行的,那么是怎么分割信息呢?其实,在rdb每一部分之前都有一个类型字节,在Redis中称为opcodes,即操作符。
下表为RDB支持的部分操作符
16进制 | 10进制 | 简称 | 说明 |
---|---|---|---|
0xFF | 255 | EOF | rdb 文件结束符 |
0xFE | 254 | SELECTDB | 标识文件中后续键值对所属的数据库编号 |
0xFD | 253 | EXPIRETIME | redis过期时间,使用秒表示。 |
0xFC | 252 | EXPIRETIMEMS | redis 过期时间,使用毫秒表示。 |
0xFB | 251 | RESIZEDB | redis dbsize,描述 key 数目和设置了过期时间 key 数目 |
0xFA | 250 | AUX | 辅助字段类型,可以存储任意的的 key-value 对 |
0xF9 | 249 | FREQ | 标识LFU访问频率信息 |
0xF8 | 248 | IDLE | 标识LRU空闲时间 |
RDB 文件头主要是描述信息,如 redis 版本、rdb 创建时间、内存大小等信息的内容,首先是魔数REDIS,后面的00009对应记录了 rdb 文件的版本,到目前为止Redis共有9个版本,各个版本差异说明请参考Redis RDB Version History,后面是redis版本6.2.6以及其他信息。AUX 指令即操作符:0xFA,描述 redis 属性,可存在多个 key-value 组合
上图中数据区只有一个key,即key1:value1,实际rdb数据区是编码最复杂的区域,采用数十种不同编码方法。0xFE指定数据库编号,示例值 “00” 表示接下来是第 0 个库的数据。0xFB是RESIZEDB 指令,描述总的 key 个数以及有过期时间的 key 个数,总数有1个key,且有过期时间,0xFC 是EXPIRETIMEMS指令,key的过期时间,使用毫秒表示。0xF8是LFU frequency指令,key的访问频率,由于Redis 淘汰策略LRU和LFU共用一个数据结构,58同城线上Redis目前使用的淘汰策略是volatile-lru,所以通过解析rdb文件很容易获得key的访问时间,从而获得key的冷热程度,如果生产中采用scan或者key *等会造成访问时间被污染,从而失去参考意义。
相对于头信息区和数据区,RDB尾信息区简单很多,只有文件描述符0xFF 和8 byte的CRC 64 校验码。
Redis rdb数据区内容主要为键值对,下图展示rdb保存key-value的各个元素的操作符和元素的顺序。
一些key没有过期策略就不会有EXPIERE_TIME元素,在Redis4.0以前rdb文件是不记录LFU和LRU的。另外由于LFU和LRU共用一个数据结构,所以两个功能只能启用一个。VALUE_TYPE为VALUE的类型,前面的xxx是opcodes的编码,具体编码可以参考下图,常见的值类型为:STRING、QUICKLIST、INTSET、SET、ZSET_ZIPLIST、ZSET_2、HASH_ZIPLIST、HASH等。
数据类型 | 编码结构 | 值类型 |
---|---|---|
OBJ_SRTRING(0) | OBJ_ENCODING_RAW(0) | RDB_TYPE_STRING(0) |
OBJ_LIST(1) | OBJ_ENCODING_QUICKLIST(9) | RDB_TYPE_LIST_QUICKLIST(14) |
OBJ_SET(2) | OBJ_ENCODING_INTSET(6) | RDB_TYPE_SET_INTSET(11) |
OBJ_SET(2) | OBJ_ENCODING_HT(2) | RDB_TYPE_SET(2) |
OBJ_ZSET(3) | OBJ_ENCODING_ZIPLIST(5) | RDB_TYPE_ZSET_ZIPLIST(12) |
OBJ_ZSET(3) | OBJ_ENCODING_SKIPLIST(7) | RDB_TYPE_ZSET_2(5) |
OBJ_HASH(4) | OBJ_ENCODING_ZIPLIST(5) | RDB_TYPE_HASH_ZIPLIST(13) |
OBJ_HASH(4) | OBJ_ENCODING_HT(2) | RDB_TYPE_HASH(4) |
OBJ_STREAM(6) | 无 | RDB_TYPE_STREAM_LISTPACKS(15) |
上表中这些字符串实际再Redis服务中都是用宏定义的,括号中即为该宏对应的值。
在Redis中key都可以看作字符串,在rdb文件中它的格式是这样的:
其中LENGTH为了节省空间,采用了变长的格式,具体参考下表:
序号 | 第一个字节 | 第二个字节 | 第三个字节 | 第四个字节 | 第五个字节 | 第六个字节 | 第七个字节 | 第八个字节 | 表示范围 | 占用字节 | 其他说明 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 00xxxxxx | - | - | - | - | - | - | - | <64 | 1 | sting格式 |
2 | 01xxxxxx | xxxxxxxx | - | - | - | - | - | - | <2^14 | 2 | sting格式 |
3 | 10000000 | xxxxxxxx | xxxxxxxx | xxxxxxxx | xxxxxxxx | - | - | - | <2^32 | 5 | sting格式 |
4 | 10000001 | xxxxxxxx | xxxxxxxx | xxxxxxxx | xxxxxxxx | xxxxxxxx | xxxxxxxx | xxxxxxxx | <2^56 | 8 | sting格式 |
5 | 11000000 | xxxxxxxx | - | - | - | - | - | - | [-27,27-1] | 2 | int格式 |
6 | 11000001 | xxxxxxxx | xxxxxxxx | - | - | - | - | - | [-215,215-1] | 3 | int格式 |
7 | 11000010 | xxxxxxxx | xxxxxxxx | xxxxxxxx | xxxxxxxx | - | - | - | [-231,231-1] | 5 | int格式 |
8 | 11000011 | LZF格式 |
另外RDB文件对字符串的保存有两种优化形式,一种是常识将字符串按照整型保存,一种是通过将字符串进行LZF压缩之后保存。
上表序号5-7是将字符串尝试用int保存的具体编码方式,下图为RDB将字符串转换为INT后的保存形式。
上表序号8是将字符串进行压缩保存的具体编码方式,首字节开头的两个bit仍然为11,后六个bit为000011。
COMPRESS_LE表明压缩之后的长度,实际就是DATA的长度,ORIGINAL_LEN这个字段记录的是原始的数据记录长度。
相对key在rdb的保存形式,value类型更多,其中字符串和key的保存相同,不再进行讨论。
列表在Redis中编码为quicklist结构,从整体看是一个双向链表,但链表的每个节点在Redis中编码为ziplist结构在一块连续的内存中保存,并且保存时可以选择进行LZF压缩或者不压缩。下图为list在rdb中的保存结构。
上图中QUICKLIST是保存的list的元素个数,如果list未压缩,ziplist1保存形式同字符串的保留形式;如果list压缩,LZF1保存形式同RDB LZF保存形式。
set在Redis中有两种编码方式:一种是intset,一种是Hash。
使用intset的编码方式较为简单,直接使用字符串的方式,Hash的编码方式较为复杂,见下图
上图中字典大小实际记录的是list元素个数,字典键只记录了key,为啥没有value,因为list的元素都是单元素,value都为null,所以没有必要记录。
zset在Redis中有两种编码方式:一种是ziplist,一种是skiplist。
使用ziplist的编码方式较为简单,直接使用字符串的方式,我重点介绍下skiplist的编码方式,见下图
skiplist长度为zset元素数量,元素已字符串类型保存,元素分值为一个双精度浮点数类型(固定为8个字节)
hash在Redis中有两种编码方式:一种是ziplist,一种是hash。
使用ziplist的编码方式较为简单,直接使用字符串的方式,我重点介绍下hash的编码方式,见下图
hash长度实际为hash的元素个数,键和值都是字符串的方式保存
从以上的各类型保存形式可以发现,虽然数据类型很多,但是底层元素保存都遵循简单的字符串保留方式,所以重点总结了KEY在rdb文件中的保留形式
通过以上的介绍,希望大家能对RDB文件有个简单的了解,并最终指导开发RDB解析程序,为开发提供更加丰富的离线key分析产品。
相关链接:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。