银川网站建设银川,网站建设举措,石碣东莞网站建设,海淀seo搜索引擎优化公司前言
相信各位对 Redis 的这两种持久化机制都不陌生#xff0c;简单来说#xff0c;RDB 就是对数据的全量备份#xff0c;AOF 则是增量备份#xff0c;而从 4.0 版本开始引入了混合方式#xff0c;以 7.2.3 版本为例#xff0c;会生成三类文件#xff1a;RDB、AOF 和记…前言
相信各位对 Redis 的这两种持久化机制都不陌生简单来说RDB 就是对数据的全量备份AOF 则是增量备份而从 4.0 版本开始引入了混合方式以 7.2.3 版本为例会生成三类文件RDB、AOF 和记录 aof 文件的元数据信息文件如下图所示这时的 AOF 可以看作是一种差异备份。 接下来本文将结合具体的备份文件通过分析其结构从另一种角度来看两种持久化方式的差异。
RDB
首先是对 RDB 全量备份文件的解析想要生成 RDB 文件有两种方式一种是手动方式使用 save阻塞或者 bgsave非阻塞命令生成一种是在配置文件中增加save m n表示在 m 内至少出现了 n 次变更就会执行 bgsave 命令配置来实现。
下面就以一个具体的dump.rdb在 0 号库中有一条键为 hello值为 world 的记录文件为例来解析其文件格式由于 RDB 文件是二进制格式这里使用了一个在线的十六进制编辑器进行查看 下文均是结合 Redis 7.2.3 版本的源码的 rdb.c 文件进行解析对应源码地址。 0x00 Redis 版本
52 45 44 49 53 30 30 31 31根据源码snprintf(magic,sizeof(magic),REDIS%04d,RDB_VERSION);可以看到这里前五位是固定值REDIS后四位用于标识RDB的版本对应11。
0x01 辅助信息
这部分涉及数据较多先放出源码
if (rdbSaveAuxFieldStrStr(rdb,redis-ver,REDIS_VERSION) -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb,redis-bits,redis_bits) -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb,ctime,time(NULL)) -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb,used-mem,zmalloc_used_memory()) -1) return -1;
if (rdbSaveAuxFieldStrInt(rdb, aof-base, aof_base) -1) return -1;结合编辑器右侧的信息可以发现这部分数据下图中选中的数据 redis-ver(Redis 版本) 这部分对应FA 09 72 65 64 69 73 2D 76 65 72 05 37 2E 32 32 2E 33其中开头的FA(250)代表这部分数据是 AUX 属性字段根据源码#define RDB_OPCODE_AUX 250可以了解到。然后是09 72 65 64 69 73 2D 76 65 7209 代表随后的 9个字节是属性名即redis-ver最后是05 37 2E 32 32 2E 33其中 05 代表随后的 5 个字节是属性名对应的字段值即 Redis 的版本号7.2.3。 redis-bits位架构 这部分对应FA 0A 72 65 64 69 73 2D 62 69 74 73 C0 40。参考 1 可知开始的FA代表AUXOA代表随后的 10 字节是属性名即redis-bits。但是随后的C0就不再是代表值的长度了这里先说明C0代表后续的一个字节按照整数进行读取对应0x4064即代表是 Redis 的 64位架构。下面我们再来说明为什么会有以上的区别 其实代表值长度的不一定只有一个字节这里会根据前两位进行判断C0 对应1100 0000 如果前两位是 00 那么后续的 6 位可表示 0 ~ 63就代表实际的字符串长度。 如果前两位是 01那么接下来的一个字节也会用于表示长度加上第一个剩下的 6 位总共 14 位可表示0 ~ 16383代表实际的字符串长度。 如果前两位是 10那么剩下 6 位的值如果是 0就代表随后的 32 字节代表具体长度如果剩下 6 位的值是 1就代表随后的 64 字节代表具体长度。 如果前两位是 11则需要根据整个字节的值再进行判断如果是C0就代表将随后的 1 字节表示整数如果是 C1 就代表随后的 2 字节表示整数如果是 C2 就代表随后的 4 字节表示整数如果是C3就代表随后的内容是使用LZF 压缩算法处理后的内容。 ctime文件创建时间 这部分对应FA 05 63 74 69 6D 65 C2 44 11 57 65参考 1 可知开始的FA代表AUX05代表随后的 5 字节是属性名即ctime。参考 2 中解析可知随后的C2代表后续的 4 字节即44 11 57 65表示整数由于需要按照小端序读取因此对应的内容是 0x65571144即秒级时间戳如下图所示 used-mem内存使用大小 这部分对应FA 08 75 73 65 64 2D 6D 65 6D C2 40 15 12 00参考 1 可知开始的FA代表AUX08代表随后的 8 字节是属性名即used-mem。参考 3 可知随后的C2代表后续的 4 字节即40 15 12 00表示整数对应的内容是 0x00121540即 Redis 在 创建 rdb 文件前占用的内存是 1185088 字节1.13 MB。 aof-base 是否为 aof 基准文件 这部分对应FA 08 61 6F 66 2D 62 61 73 65 C0 00参考 1 可知开始的FA代表AUX08代表随后的 8 字节是属性名即aof-base。参考 2 中解析可知随后的C0代表后续的 1 字节即00表示整数即该 RDB 文件不是作为 AOF 的基准文件后文中可以看到在 AOF 中生成的 RDB 文件中该值为 1。
0x02 数据部分
FE 00 FB 01 00 00 05 68 65 6C 6C 6F 05 77 6F 72 6C 64这部分开始对应具体的数据信息先展示源码
/* save all databases, skip this if were in functions-only mode */
if (!(req SLAVE_REQ_RDB_EXCLUDE_DATA)) {for (j 0; j server.dbnum; j) {if (rdbSaveDb(rdb, j, rdbflags, key_counter) -1) goto werr;}
}// 以下内容是 rdbSaveDb 函数内的语句/* Write the SELECT DB opcode */
if ((res rdbSaveType(rdb,RDB_OPCODE_SELECTDB)) 0) goto werr;
written res;
if ((res rdbSaveLen(rdb, dbid)) 0) goto werr;
written res;
/* Write the RESIZE DB opcode. */
unsigned long long expires_size dbSize(db, DB_EXPIRES);
if ((res rdbSaveType(rdb,RDB_OPCODE_RESIZEDB)) 0) goto werr;
written res;
if ((res rdbSaveLen(rdb,db_size)) 0) goto werr;
written res;
if ((res rdbSaveLen(rdb,expires_size)) 0) goto werr;
written res;可以看出这部分是遍历所有的数据库内容然后进行保存下面再结合具体的内容进行介绍。
首先是FE 00其中FE(254)对应RDB_OPCODE_SELECTDB常量是查询数据库的标志00即代表 0 号数据库。
然后是FB 01 00其中FB(251)对应RDB_OPCODE_RESIZEDB常量是查询该数据库大小的标志根据if ((res rdbSaveLen(rdb,db_size)) 0) goto werr;知道01代表数据库的大小即只有一条数据根据if ((res rdbSaveLen(rdb,expires_size)) 0) goto werr;知道00代表没有包含过期标志的数据。
最后是00 05 68 65 6C 6C 6F 05 77 6F 72 6C 64代表具体的数据内容。其中开始的00代表类型是字符串参考源码可知RDB_TYPE_STRING 的值是 0
/* Save the object type of object o. */
int rdbSaveObjectType(rio *rdb, robj *o) {switch (o-type) {case OBJ_STRING:return rdbSaveType(rdb,RDB_TYPE_STRING);case OBJ_LIST:if (o-encoding OBJ_ENCODING_QUICKLIST || o-encoding OBJ_ENCODING_LISTPACK)return rdbSaveType(rdb, RDB_TYPE_LIST_QUICKLIST_2);elseserverPanic(Unknown list encoding);case OBJ_SET:if (o-encoding OBJ_ENCODING_INTSET)return rdbSaveType(rdb,RDB_TYPE_SET_INTSET);else if (o-encoding OBJ_ENCODING_HT)return rdbSaveType(rdb,RDB_TYPE_SET);else if (o-encoding OBJ_ENCODING_LISTPACK)return rdbSaveType(rdb,RDB_TYPE_SET_LISTPACK);elseserverPanic(Unknown set encoding);case OBJ_ZSET:if (o-encoding OBJ_ENCODING_LISTPACK)return rdbSaveType(rdb,RDB_TYPE_ZSET_LISTPACK);else if (o-encoding OBJ_ENCODING_SKIPLIST)return rdbSaveType(rdb,RDB_TYPE_ZSET_2);elseserverPanic(Unknown sorted set encoding);case OBJ_HASH:if (o-encoding OBJ_ENCODING_LISTPACK)return rdbSaveType(rdb,RDB_TYPE_HASH_LISTPACK);else if (o-encoding OBJ_ENCODING_HT)return rdbSaveType(rdb,RDB_TYPE_HASH);elseserverPanic(Unknown hash encoding);case OBJ_STREAM:return rdbSaveType(rdb,RDB_TYPE_STREAM_LISTPACKS_3);case OBJ_MODULE:return rdbSaveType(rdb,RDB_TYPE_MODULE_2);default:serverPanic(Unknown object type);}return -1; /* avoid warning */
}随后的05 68 65 6C 6C 6F中的 05表示键的长度是5对应68 65 6C 6C 6F即hello。最后的05 77 6F 72 6C 64代表值的长度也是 5内容是77 6F 72 6C 64即world。
0x03 尾部信息
FF 18 7F 33 2E 0F C6 20 19根据源码#define RDB_OPCODE_EOF 255可知FF(25)是文件的 EOF 即结束标志。随后的 8 位根据源码可知对应 CRC64 校验码
/* EOF opcode */
if (rdbSaveType(rdb,RDB_OPCODE_EOF) -1) goto werr;/* CRC64 checksum. It will be zero if checksum computation is disabled, the* loading code skips the check in this case. */
cksum rdb-cksum;
memrev64ifbe(cksum);
if (rioWrite(rdb,cksum,8) 0) goto werr;AOF
AOF 用于对数据库的增量备份如果需要开启需要将配置文件中的appendonly设置为 yes。同时根据需要可以设置appenddirname对应保存的文件夹设置appendfilename用于配置文件名设置appendfsync 用于配置频率。开启后可以在指定的文件夹下看到类似以下的文件结构 其中 rdb 结尾的代表是 AOF 备份的基准文件aof 文件是增量备份的执行命令信息manifest 文件是记录 aof 文件的元数据信息。
0x00 dump.aof.1.base.rdb
通过十六进制编辑器打开该文件可以发现内容和 RDB 中的格式一致创建数据前备份的所以没有数据部分 而由于是 AOF 的基准文件这里aof-base的值是01即代表是基准文件。
0x01 dump.aof.1.incr.aof
文本文件内容如下*开头代表命令包含的参数个数$开头代表命令的长度
*2 // 两个参数
$6 // 第一个参数长度为 6, 对应 SELECT 的长度
SELECT
$1 // 第二个参数长度为 1, 对应 0, 即 0 号数据库
0
*3 // 三个参数
$3 // 第一个参数长度为 3, 对应 set 的长度
set
$5 // 第二个参数长度为 5, 对应 hello 的长度
hello
$0 // 第三个参数长度为 0*3 // 三个参数
$3 // 第一个参数长度为 3, 对应 set 的长度
set
$5 // 第二个参数长度为 5, 对应 hello 的长度
hello
$5
world // 第三个参数长度为 5, 对应 world 的长度0x02 dump.aof.manifest
文本文件内容如下
file dump.aof.1.base.rdb seq 1 type b
file dump.aof.1.incr.aof seq 1 type i其中seq 1 代表文件序号为 1type b代表type base即基准文件type i代表type increment即增量文件。
总结
本文根据一个简单的 RDB 文件讲解了 RDB 文件的存储格式同时也简单介绍了 AOF 的文件格式。关于 RDB 中的 LZF 压缩算法和更复杂数据的存储方式包含过期时间数据类型为 SetMap等未作介绍将留到下次。