redis对象编码源码阅读——字符串编码与创建
1. 字符串的编码
类型 | 编码 | 对象 |
---|---|---|
REDIS_STRING | REDIS_ENCODING_INT | 使用整数值实现的字符串对象 |
REDIS_STRING | REDIS_ENCODING_EMBSTR | 使用embstr编码的简单动态字符串实现的字符串对象 |
REDIS_STRING | REDIS_ENCODING_RAW | 使用简单动态字符串实现的字符串对象 |
摘自《Redis设计与实现》第63页
2. 创建字符串对象
在创建字符串对象的时候,有两种创建方式,一种是EMBSTR
,一种是RAW
.
REDIS_ENCODING_EMBSTR_SIZE_LIMIT
是一个比较小的值39。所以两者最大的区别就是长度。EMBSTR
表示短字符串,RAW
表示长字符串。
robj *createStringObject(char *ptr, size_t len) {
if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
3. EMBSTR
字符串创建
在分配空间的时候,不仅分配了robj
的空间,还额外分配了sdshdr
的空间,并把o->ptr
直接指向了sdshdr
对象。
/* Create a string object with encoding REDIS_ENCODING_EMBSTR, that is
* an object where the sds string is actually an unmodifiable string
* allocated in the same chunk as the object itself. */
robj *createEmbeddedStringObject(char *ptr, size_t len) {
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1);
// 正好指向 sdshdr 数据结构
struct sdshdr *sh = (void*)(o+1);
o->type = REDIS_STRING;
o->encoding = REDIS_ENCODING_EMBSTR;
// 正好指向 sdshdr 的字符串
o->ptr = sh+1;
o->refcount = 1;
o->lru = LRU_CLOCK();
// len: 表示总长度
// free: 表示剩余长度
sh->len = len;
sh->free = 0;
if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = '\0';
} else {
memset(sh->buf,0,len+1);
}
return o;
}
4. RAW
字符串创建
无编码字符串只分配了robj
对象的空间,并直接把o->ptr
指向了字符串对象char*
。
这里和《Redis设计与实现》65页的图8-2,存储结构矛盾了。我认为这里创建的RAW
对象之后的结构肯定还是会变化的,之后会再次申请sdshdr
的空间,与书上一样。
/* Create a string object with encoding REDIS_ENCODING_RAW, that is a plain
* string object where o->ptr points to a proper sds string. */
robj *createRawStringObject(char *ptr, size_t len) {
return createObject(REDIS_STRING,sdsnewlen(ptr,len));
}
robj *createObject(int type, void *ptr) {
robj *o = zmalloc(sizeof(*o));
o->type = type;
o->encoding = REDIS_ENCODING_RAW;
o->ptr = ptr;
o->refcount = 1;
/* Set the LRU to the current lruclock (minutes resolution). */
o->lru = LRU_CLOCK();
return o;
}
5. EMBSTR
编码的好处
-
embstr编码将创建字符串对象所需的内存分配次数从raw编码的两次降低为一次
-
释放embstr编码的字符串对象只需要调用一次内存释放函数,而释放raw编码的字符串对象需要调用两次内存释放函数
-
因为embstr编码的字符串对象的所有数据都保存在一块连续的内存里面,所以这种编码的字符串对象比起raw编码的字符串对象能够更好的利用缓存带来的优势
以上摘自《Redis设计与实现》第65页,详情见65页
-
EMBSTR
编码字符串的长度可能非常长,也可能比较短,跨度是非常大的,所以每次(我猜测)字符串内容改变的时候,都需要判断字符串是不是需要释放多余的空间,如果可以的话还可以换成其他编码。即调用tryObjectEncoding函数 -
而在tryObjectEncoding函数中,如果要释放多余空间的话,函数会先释放之前的
char*
空间,再申请新的空间,这也是RAW
与EMBSTR
的区别