Rdb.c:31
int rdbSaveLen(FILE *fp, uint32_t len) { unsigned char buf[2]; int nwritten; if (len < (1<<6)) { /* Save a 6 bit len */ buf[0] = (len&0xFF)|(REDIS_RDB_6BITLEN<<6); if (rdbWriteRaw(fp,buf,1) == -1) return -1; nwritten = 1; } else if (len < (1<<14)) { /* Save a 14 bit len */ buf[0] = ((len>>8)&0xFF)|(REDIS_RDB_14BITLEN<<6); buf[1] = len&0xFF; if (rdbWriteRaw(fp,buf,2) == -1) return -1; nwritten = 2; } else { /* Save a 32 bit len */ buf[0] = (REDIS_RDB_32BITLEN<<6); if (rdbWriteRaw(fp,buf,1) == -1) return -1; len = htonl(len); if (rdbWriteRaw(fp,&len,4) == -1) return -1; nwritten = 1+4; } return nwritten; }也许你发现了,上边的表格中只有3种,还有一种哪去了呢?
把这种特别放开是因为这种比较特殊
类型位表示 类型整型表示 字节后6位含义 类型解析11 3 编码类型 如果字符串是通过编码后存储的,则存储长度的类型的位表示为11,然后根据后6位的编码类型来确定怎样读取和解析接下来的数据
是不是觉得这种长度类型很奇怪,为什么要这样做?
Redis在两种情况下需要对存储的内容进行编码
1.把字符串转成整数存储
比如:‘-100’需要4个字节存储,转换整数只需要一个字节
相关函数rdbTryIntegerEncoding(rdb.c:88)
2.使用lzf算法压缩字符串
相关函数lzf_compress(lzf_c.c:99),lzf的算法解释见lzf字符串压缩算法
当redis使用这两种编码对字符串进行编码时,在读取时需要区分改字符串有没有被编码过,对编码过的字符串需要特别处理,因为长度信息是存储在字符串的前面得,所以可以通过在存储长度的位置上加入编码类型的信息。
我们来看看相关代码
Rdb.c:557
uint32_t rdbLoadLen(FILE *fp, int *isencoded) { unsigned char buf[2]; uint32_t len; int type; if (isencoded) *isencoded = 0; if (fread(buf,1,1,fp) == 0) return REDIS_RDB_LENERR; type = (buf[0]&0xC0)>>6; if (type == REDIS_RDB_6BITLEN) { /* Read a 6 bit len */ return buf[0]&0x3F; } else if (type == REDIS_RDB_ENCVAL) { /* Read a 6 bit len encoding type */ if (isencoded) *isencoded = 1; return buf[0]&0x3F; } else if (type == REDIS_RDB_14BITLEN) { /* Read a 14 bit len */ if (fread(buf+1,1,1,fp) == 0) return REDIS_RDB_LENERR; return ((buf[0]&0x3F)<<8)|buf[1]; } else { /* Read a 32 bit len */ if (fread(&len,4,1,fp) == 0) return REDIS_RDB_LENERR; return ntohl(len); } }我们可以看到,在读取rdb文件时,当发现长度类型是REDIS_RDB_ENCVAL,把编码类型返回。
我们来看看知道编码类型后的处理
Rdb.c:633
robj *rdbGenericLoadStringObject(FILE*fp, int encode) { int isencoded; uint32_t len; sds val; len = rdbLoadLen(fp,&isencoded); if (isencoded) { switch(len) { case REDIS_RDB_ENC_INT8: case REDIS_RDB_ENC_INT16: case REDIS_RDB_ENC_INT32: return rdbLoadIntegerObject(fp,len,encode); case REDIS_RDB_ENC_LZF: return rdbLoadLzfStringObject(fp); default: redisPanic("Unknown RDB encoding type"); } } if (len == REDIS_RDB_LENERR) return NULL; val = sdsnewlen(NULL,len); if (len && fread(val,len,1,fp) == 0) { sdsfree(val); return NULL; } return createObject(REDIS_STRING,val); }读取长度
如果长度类型是有编码信息的,则根据编码类型进行读取
如果长度类型是有效长度,则根据长度信息读取字符串
REDIS_EXPIRETIME类型如果一个key被expire设置过,那么在该key与value的前面会有一个REDIS_EXPIRETIME类型与其对应的值。
REDIS_EXPIRETIME类型对应的值是过期时间点的timestamp
REDIS_EXPIRETIME类型与其值是可选的,不是必须的,只有被expire设置过的key才有这个值
假设有一个key被expire命令设置过,把这REDIS_EXPIRETIME类型代入到上边的rdb文件的格式中,那么rdb文件的整体格式变成为:
文件签名 | 版本号 | REDIS_SELECTDB类型 | db编号 | REDIS_EXPIRETIME类型 | timestamp | 类型 | 值 | … | REDIS_SELECTD 类型 | db编号 | 类型 | 值 | … | REDIS_EOF类型
数据类型数据类型主要有以下类型:
REDIS_STRING类型
REDIS_LIST类型
REDIS_SET类型
REDIS_ZSET类型
REDIS_HASH类型
REDIS_VMPOINTER类型
REDIS_HASH_ZIPMAP类型
REDIS_LIST_ZIPLIST类型
REDIS_SET_INTSET类型
REDIS_ZSET_ZIPLIST类型