Redis RDB 文件存储实现

redis通过把内存数据保存为rdb文件来实现快照,是整个redis内核中不可缺少的一部分,为实现服务高可用提供了强有力的支撑。有以下2点作用:
1. 持久化 (比 aof 恢复速度更快,也占用更小的磁盘空间,因此作为默认的持久化方式)

2. 主从同步 (开启主从后,需要首先把master上的历史数据同步到slave,这时slave就会拉取master的rdb文件,然后解析加载rdb文件到内存)

rdb 保存了真实的redis数据,相当于mysql的innodb引擎的作用,可见其重要性非同一般。rdb也可以用来离线分析redis中数据分布情况。对于如此重要的文件,其中的数据存储格式我们当然要了如指掌,这样才能运筹帷幄,让可用性达到 4个9 (99.99%), 5个9(99.999%)

到目前为止,主要的rdb 版本是 v6  v7,从redis v3.2 版本开始使用 rdb v7,之前的redis v2.8 v3.0 使用的rdb版本都是rdb v6,文件的大致格式如下:

 rdb v6 

  • A 9 bytes magic "REDIS0006"  (9字节的redis标记,其中末四位是rdb版本信息)
  • key-value pairs (redis 真实的数据,以key value 成对存储)
  • An EOF opcode (代表结束的标记)
  • CRC64 checksum (用于对整个文件完整性校验的字符串)

rdb v7 

  • A 9 bytes magic "REDIS0007" (9字节的redis标记,其中末四位是rdb版本信息)
  • Info field 1 (整个rdb文件中为了保存一些额外扩展信息而设计)
  • Info field 2
  • ...
  • Info field N
  • Info field end-of-fields
  • key-value pairs (redis真实数据,以key value 成对存储)
  • An EOF opcode (代表结束的标记)
  • CRC64 checksum (用于对整个文件完整性校验的字符串)
先来看看其中真实数据是如何存储的,也就是上面的 key-value pairs ,使用过json 的同学都知道这个保存非常简单,比如:
{
    "key1":"value1",
    "key2":"value2",
    "key3":"value3"
}

但这里并不是这样的结构,是一个连续的按字节存储的序列。如果连续存储,其中没有其他标记,如何区分 key 与 value的边界呢?换句话也就是如何知道key这个字串哪里结束,value从哪里开始呢?redis使用了提前记录字节长度的方式实现,也就是 key-len key value-len value 这种的形式,然后按对应长度读取字符。问题又来了,redis的key的最大长度是多少呢,value的最大长度是多少呢,好像也没有明确的限制,如果你不建议性能损失的话。

redis 使用了多字节的方式保存长度(1字节、2字节、5字节、9字节 ),根据不同长度使用不同的字节,但是这里面又会出现边界问题,当从rdb文件中读取2字节,如何区分这是1字节长度值+1字节key值,还是2字节的长度值呢?到了这里似乎更加迷茫,好像陷入了一个死循环,好像无解。

如果了解UTF8编码实现原理,你一定会有可行的解决方案。是的,我们先来看看一段redis源码,因为对一个系统最好的理解就是代码+注释,更何况redis中的注释也是相当的详细,基本一看就懂。

/* The current RDB version. When the format changes in a way that is no longer
 * backward compatible this number gets incremented. */
#define RDB_VERSION 8

/* Defines related to the dump file format. To store 32 bits lengths for short
 * keys requires a lot of space, so we check the most significant 2 bits of
 * the first byte to interpreter the length:
 *
 * 00|XXXXXX => if the two MSB are 00 the len is the 6 bits of this byte
 * 01|XXXXXX XXXXXXXX =>  01, the len is 14 byes, 6 bits + 8 bits of next byte
 * 10|000000 [32 bit integer] => A full 32 bit len in net byte order will follow
 * 10|000001 [64 bit integer] => A full 64 bit len in net byte order will follow
 * 11|OBKIND this means: specially encoded object will follow. The six bits
 *           number specify the kind of object that follows.
 *           See the RDB_ENC_* defines.
 *
 * Lengths up to 63 are stored using a single byte, most DB keys, and may
 * values, will fit inside. */
#define RDB_6BITLEN 0
#define RDB_14BITLEN 1
#define RDB_32BITLEN 0x80
#define RDB_64BITLEN 0x81
#define RDB_ENCVAL 3
#define RDB_LENERR UINT64_MAX

/* When a length of a string object stored on disk has the first two bits
 * set, the remaining six bits specify a special encoding for the object
 * accordingly to the following defines: */
#define RDB_ENC_INT8 0        /* 8 bit signed integer */
#define RDB_ENC_INT16 1       /* 16 bit signed integer */
#define RDB_ENC_INT32 2       /* 32 bit signed integer */
#define RDB_ENC_LZF 3         /* string compressed with FASTLZ */ 

在上面的注释中有原作者的介绍,我在这里在按我自己的理解解释下:
1. 
2.
3.
4.




未完待续

标签: redis

25
Jan 2018
AUTHOR WiFeng
CATEGORY C/C++,Redis
COMMENTS No Comments

添加新评论 »

   点击刷新验证码