10.4 分析RDB文件

通过上一节对RDB文件的介绍,我们现在应该对RDB文件中的各种内容和结构有一定的了解了,是时候抛开单纯的图片示例,开始分析和观察一下实际的RDB文件了。

我们使用od命令来分析Redis服务器产生的RDB文件,该命令可以用给定的格式转存(dump)并打印输入文件。比如说,给定-c参数可以以ASCII编码的方式打印输入文件,给定-x参数可以以十六进制的方式打印输入文件,诸如此类,具体的信息可以参考od命令的文档。

10.4.1 不包含任何键值对的RDB文件

让我们首先从最简单的情况开始,执行以下命令,创建一个数据库状态为空的RDB文件:



redis> FLUSHALL
OK
redis> SAVE
OK

然后调用od命令,打印RDB文件:



$ od -c dump.rdb
0000000   R E D I S 0 0 0 6 377 334 263 C 360 Z 334
0000020 362 V
0000022

根据之前学习的RDB文件结构知识,当一个RDB文件没有包含任何数据库数据时,这个RDB文件将由以下四个部分组成:

·五个字节的"REDIS"字符串。

·四个字节的版本号(db_version)。

·一个字节的EOF常量。

·八个字节的校验和(check_sum)。

从od命令的输出中可以看到,最开头的是“REDIS”字符串,之后的0006是版本号,再之后的一个字节377代表EOF常量,最后的334 263 C 360 Z 334 362 V八个字节则代表RDB文件的校验和。

10.4.2 包含字符串键的RDB文件

这次我们来分析一个带有单个字符串键的数据库:



redis> FLUSHALL
OK
redis> SET MSG "HELLO"
OK
redis> SAVE
OK

再次执行od命令:



$ od -c dump.rdb
0000000   R   E  D  I  S  0   0   0  6 376  \0 \0 003  M   S  G
0000020 005   H  E  L  L  O 377 207  z  =  304  f   T  L 343
0000037

根据之前学习的数据库结构知识,当一个数据库被保存到RDB文件时,这个数据库将由以下三部分组成:

·一个一字节长的特殊值SELECTDB。

·一个长度可能为一字节、两字节或者五字节的数据库号码(db_number)。

·一个或以上数量的键值对(key_value_pairs)。

观察od命令打印的输出,RDB文件的最开始仍然是REDIS和版本号0006,之后出现的376代表SELECTDB常量,再之后的\0代表整数0,表示被保存的数据库为0号数据库。

在数据库号码之后,直到代表EOF常量的377为止,RDB文件包含有以下内容:



\0 003 M S G 005 H E L L O

根据之前学习的键值对结构知识,在RDB文件中,没有过期时间的键值对由类型(TYPE)、键(key)、值(value)三部分组成:其中类型的长度为一字节,键和值都是字符串对象,并且字符串在未被压缩前,都是以字符串长度为前缀,后跟字符串内容本身的方式来储存的。

根据这些特征,我们可以确定\0就是字符串类型的TYPE值REDIS_RDB_TYPE_STRING(这个常量的实际值为整数0),之后的003是键MSG的长度值,再之后的005则是值HELLO的长度。

10.4.3 包含带有过期时间的字符串键的RDB文件

现在,让我们来创建一个带有过期时间的字符串键:



redis> FLUSHALL
OK
redis> SETEX MSG 10086 "HELLO"
OK
redis> SAVE
OK

打印RDB文件:



$ od -c dump.rdb
0000000   R   E  D   I   S   0   0   0  6 376 \0 374  \  2 365 336
0000020   @ 001 \0  \0  \0 003   M   S  G 005  H   E  L  L   O 377
0000040 212 231  x 247 252   } 021 306
0000050

根据之前学习的键值对结构知识,一个带有过期时间的键值对将由以下部分组成:

·一个一字节长的EXPIRETIME_MS特殊值。

·一个八字节长的过期时间(ms)。

·一个一字节长的类型(TYPE)。

·一个键(key)和一个值(value)。

根据这些特征,可以得出RDB文件各个部分的意义:

·REDIS0006:RDB文件标志和版本号。

·376\0:切换到0号数据库。

·374:代表特殊值EXPIRETIME_MS。

·\2 365 336@001\0\0:代表八字节长的过期时间。

·\0 003 M S G:\0表示这是一个字符串键,003是键的长度,MSG是键。

·005 H E L L O:005是值的长度,HELLO是值。

·377:代表EOF常量。

·212 231 x 247 252 } 021 306:代表八字节长的校验和。

10.4.4 包含一个集合键的RDB文件

最后,让我们试试在RDB文件中包含集合键:



redis> FLUSHALL
OK
redis> SADD LANG "C" "JAVA" "RUBY"
(integer) 3
redis> SAVE
OK

打印输出如下:



$ od -c dump.rdb
0000000   R   E   D   I   S  0   0   0 6 376 \0 002 004 L   A   N
0000020   G 003 004   R   U  B   Y 004 J   A  V   A 001 C 377 202
0000040 312   r 352 346 305  * 023
0000047

以下是RDB文件各个部分的意义:

·REDIS0006:RDB文件标志和版本号。

·376\0:切换到0号数据库。

·002 004 L A N G:002是常量REDIS_RDB_TYPE_SET(这个常量的实际值为整数2),表示这是一个哈希表编码的集合键,004表示键的长度,LANG是键的名字。

·003:集合的大小,说明这个集合包含三个元素。

·004 R U B Y:集合的第一个元素。

·004 J A V A:集合的第二个元素。

·001 C:集合的第三个元素。

·377:代表常量EOF。

·202 312 r 352 346 305*023:代表校验和。

10.4.5 关于分析RDB文件的说明

因为Redis本身带有RDB文件检查工具redis-check-dump,网上也能找到很多处理RDB文件的工具,所以人工分析RDB文件的内容并不是学习Redis所必须掌握的技能。

不过从学习RDB文件的角度来看,人工分析RDB文件是一个不错的练习,这种练习可以帮助我们熟悉RDB文件的结构和格式,如果读者有兴趣的话,可以在理解本章的内容之后,适当地尝试一下。

最后要提醒的是,前面我们一直用od命令配合-c参数来打印RDB文件,因为使用ASCII编码打印RDB文件可以很容易地发现文件中的字符串内容。

但是,对于RDB文件中的数字值,比如校验和来说,通过ASCII编码来打印它并不容易看出它的真实值,更好的办法是使用-cx参数调用od命令,同时以ASCII编码和十六进制格式打印RDB文件:



$ od -cx dump.rdb
0000000   R  E D  I S  0 0  0  6 377  334 263  C 360  Z 334
          4552 4944 3053 3030   ff36     b3dc   f043   dc5a
0000020 362  V
          56f2
0000022

现在可以从输出中看出,RDB文件的校验和为0x 56f2 dc5a f043 b3dc(校验和以小端方式保存),这比用ASCII编码打印出来的334 263 C360 Z 334 362 V要清晰得多,后者看起来就像乱码一样。