redis6.0.5之存储格式rdb

/* The current RDB version. When the format changes in a way that is no longer
 * backward compatible this number gets incremented. */
#define RDB_VERSION 9
当前的RDB版本。当这个数字增加时,格式的该改变不再向后兼容。

/* 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:
和dump文件格式有关的定义,用32bit长度长度保存短的关键字需要很多空间,
所以我们采用检查第一个字节的最高两bit来区分长度 (节约空间)
 * 00|XXXXXX => if the two MSB are 00 the len is the 6 bits of this byte
如果最高两位为00,那么用这个字节的剩下6bit来表示长度
 * 01|XXXXXX XXXXXXXX =>  01, the len is 14 byes, 6 bits + 8 bits of next byte
如果最高两位是01,那么长度为14bit,这个字节的剩下6bit和下个字节的8bit
 * 10|000000 [32 bit integer] => A full 32 bit len in net byte order will follow
如果最高两位是10,剩下6bit为000000,那么长度为32bit,接下来的4个字节的32bit
 * 10|000001 [64 bit integer] => A full 64 bit len in net byte order will follow
如果最高两位是10,剩下6bit为000001,那么长度为64bit,接下来的6个字节的64bit
 * 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.
格式为11|OBKIND代表接下来有特殊格式的对象。这六个bit的数字代表了接下来特定类型的对象,
具体请参见RDB_ENC_*的定义
 * Lengths up to 63 are stored using a single byte, most DB keys, and may
 * values, will fit inside. */
长度小于等于63都可以用一个字节存储,大部分DB的关键字和值,都可以适用
#define RDB_6BITLEN 0           00|XXXXXX
#define RDB_14BITLEN 1            01|XXXXXX        
#define RDB_32BITLEN 0x80        10|000000
#define RDB_64BITLEN 0x81        10|000001
#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: */
当保存在磁盘上的一个字符串对象长度的前两bit已经设定的情况下(按照上面所说是11),
剩下的6bit为这个对象确定了一个特别的编码,定义如下
#define RDB_ENC_INT8 0        /* 8 bit signed integer */   8bit的符号整数
#define RDB_ENC_INT16 1       /* 16 bit signed integer */  16bit的符号整数
#define RDB_ENC_INT32 2       /* 32 bit signed integer */  32bit的符号整数
#define RDB_ENC_LZF 3         /* string compressed with FASTLZ */ 用FASTLZ压缩的字符串

***************************************************************************************
/* Produces a dump of the database in RDB format sending it to the specified
 * Redis I/O channel. On success C_OK is returned, otherwise C_ERR
 * is returned and part of the output, or all the output, can be
 * missing because of I/O errors.
产生一个数据库RDB格式的备份,把它发送到特定的redis io渠道。如果成功返回C_OK, 否则返回C_ERR,
这种情况可能导致部分输出或者全部输出因为IO错误而缺失。
 * When the function returns C_ERR and if 'error' is not NULL, the
 * integer pointed by 'error' is set to the value of errno just after the I/O
 * error. */
当函数返回C_ERR并且error非空的情况下,在IO错误发生时这个error指向的整数被设置为errno的值。

int rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi) {
    dictIterator *di = NULL;
    dictEntry *de;
    char magic[10];
    int j;
    uint64_t cksum;
    size_t processed = 0;

    if (server.rdb_checksum)  //这里计算64位的crc校验码
        rdb->update_cksum = rioGenericUpdateChecksum;
        
    snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);
    
    if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;   
    //写入redisRDB格式的版本 这里为REDIS0009
    
    if (rdbSaveInfoAuxFields(rdb,rdbflags,rsi) == -1) goto werr;
    //写入辅助域  参见  #define RDB_OPCODE_AUX        250   /* RDB aux field. */
    
    if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_BEFORE_RDB) == -1) goto werr;
    //写入辅助模块信息

    //开始写入具体数据
    for (j = 0; j < server.dbnum; j++) {
        redisDb *db = server.db+j;
        dict *d = db->dict;
        if (dictSize(d) == 0) continue;
        di = dictGetSafeIterator(d);

        /* Write the SELECT DB opcode */ 
        开始写入选择数据库库的标志代码
        if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;
        //#define RDB_OPCODE_SELECTDB   254   /* DB number of the following keys. */
        //FE 
        
        if (rdbSaveLen(rdb,j) == -1) goto werr;
        //第一个库的编号是0 写入00
        //第15个库 FE 0F

        /* Write the RESIZE DB opcode. */
        uint64_t db_size, expires_size;
        db_size = dictSize(db->dict);
        expires_size = dictSize(db->expires);
        if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;
        //#define RDB_OPCODE_RESIZEDB   251   /* Hash table resize hint. */
        
        
        //写入当前库的key个数  08
        if (rdbSaveLen(rdb,db_size) == -1) goto werr;
        //写入当前库的过期key个数  00
        if (rdbSaveLen(rdb,expires_size) == -1) goto werr;

        /* Iterate this DB writing every entry */
        while((de = dictNext(di)) != NULL) {
            sds keystr = dictGetKey(de);
            robj key, *o = dictGetVal(de);
            long long expire;

            initStaticStringObject(key,keystr);
            expire = getExpire(db,&key);
            if (rdbSaveKeyValuePair(rdb,&key,o,expire) == -1) goto werr;
            //保存key t1  
            //00是类型 表示字符串 #define RDB_TYPE_STRING 0
            //02是编码长度   t1是本身的值
            //C0 长度类型  6F 就是十进制的111
            //00字符串类型 04 key的长度 具体值 t105  C0整数长度类型 具体数值 69 6*16+9=105
            //00 05 hello   05  world
            // 00 04 t101   C0 65  
            // 00 04 t103  C0 67 
            //00 04 104 C0 68
            //00 02 t2  C1 AE 08  (10*16+14) + 8 * 256 =  2222
            // 00 04 t102  C0 66 
            --------------------------
            0F库的内容
            FB开始标志  01表示只有一个key 过期key个数 00
            00 02 t1  04  t111
            --------------------------
            /* When this RDB is produced as part of an AOF rewrite, move
             * accumulated diff from parent to child while rewriting in
             * order to have a smaller final write. */
            if (rdbflags & RDBFLAGS_AOF_PREAMBLE &&
                rdb->processed_bytes > processed+AOF_READ_DIFF_INTERVAL_BYTES)
            {
                processed = rdb->processed_bytes;
                aofReadDiffFromParent();
            }
        }
        dictReleaseIterator(di);
        di = NULL; /* So that we don't release it again on error. */
    }

    /* If we are storing the replication information on disk, persist
     * the script cache as well: on successful PSYNC after a restart, we need
     * to be able to process any EVALSHA inside the replication backlog the
     * master will send us. */
    if (rsi && dictSize(server.lua_scripts)) {
        di = dictGetIterator(server.lua_scripts);
        while((de = dictNext(di)) != NULL) {
            robj *body = dictGetVal(de);
            if (rdbSaveAuxField(rdb,"lua",3,body->ptr,sdslen(body->ptr)) == -1)
                goto werr;
        }
        dictReleaseIterator(di);
        di = NULL; /* So that we don't release it again on error. */
    }

    if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_AFTER_RDB) == -1) goto werr;

    /* EOF opcode */
    if (rdbSaveType(rdb,RDB_OPCODE_EOF) == -1) goto werr;
    //写入结尾符 FF
    #define RDB_OPCODE_EOF        255   /* End of the RDB file. */

    /* 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;
    //最后写入刚开始计算的crc64校验码
    return C_OK;

werr:
    if (error) *error = errno;
    if (di) dictReleaseIterator(di);
    return C_ERR;
}

***************************************************************************************
/* Save a few default AUX fields with information about the RDB generated. */
int rdbSaveInfoAuxFields(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
    int redis_bits = (sizeof(void*) == 8) ? 64 : 32;
    int aof_preamble = (rdbflags & RDBFLAGS_AOF_PREAMBLE) != 0;

    /* Add a few fields about the state when the RDB was created. */
    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;
    
    //我们这里的例子比较简单,字符串长度不超过20,所以没有用到压缩算法
    //写入key和value  
    //举例如下  key为"redis-ver"  value为"6.0.5"  
    //那么实际写入的情况为  RDB_OPCODE_AUX(FA) 表示辅助域的头,
    //key的长度9(09),key本身"redis-ver",value的长度5,value本身"6.0.5" 
    //接下来写入的是键redis-bits和其内容64  FA 0A redis-bits 8比特的整数(C0) 具体的值64(40) 
    //接下来写入的是 ctime  那么  FA 05 然后是ctime,记下来是具体的值
    //因为数据量过大, #define RDB_ENC_INT32 2       /* 32 bit signed integer */  32bit的符号整数
    //所以C2代表了 1100 0010 后面几位是具体的时间秒数  00 CA B5 5F
    //接下来是used-mem和其值,FA 08 used-mem  和  具体分配的字节数 C2 68 34 0D 00
    //接下来是 aof-preamble, FA 0C , C0, C0表示用8bit,00表示具体的数值

    /* Handle saving options that generate aux fields. */
    if (rsi) {
        if (rdbSaveAuxFieldStrInt(rdb,"repl-stream-db",rsi->repl_stream_db)
            == -1) return -1;
        if (rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid)
            == -1) return -1;
        if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset)
            == -1) return -1;
    }
    if (rdbSaveAuxFieldStrInt(rdb,"aof-preamble",aof_preamble) == -1) return -1;
    return 1;
}
    
附上我们简单例子的数据
52 45 44 49 53 30 30 30 39 FA 09 72 65 64 69 73
2D 76 65 72 05 36 2E 30 2E 35 FA 0A 72 65 64 69
73 2D 62 69 74 73 C0 40 FA 05 63 74 69 6D 65 C2
00 CA B5 5F FA 08 75 73 65 64 2D 6D 65 6D C2 68
34 0D 00 FA 0C 61 6F 66 2D 70 72 65 61 6D 62 6C
65 C0 00 FE 00 FB 08 00 00 02 74 31 C0 6F 00 04
74 31 30 35 C0 69 00 05 68 65 6C 6C 6F 05 77 6F
72 6C 64 00 04 74 31 30 31 C0 65 00 04 74 31 30
33 C0 67 00 04 74 31 30 34 C0 68 00 02 74 32 C1
AE 08 00 04 74 31 30 32 C0 66 FE 0F FB 01 00 00
02 74 31 04 74 31 31 31 FF 73 10 D3 30 81 46 1B
33
对应的字符串
REDIS0009?redis-ver6.0.5?redis-bits繞?ctime?

 

posted on 2020-11-26 16:44  子虚乌有  阅读(421)  评论(0)    收藏  举报