通过Redis 6.2 key的创建SDS(源码)知道String在redis中是有多种编码方式存储的,但是实际上创建一个字符串对象是如何选择编码方式的,下面是源码
文章目录
*RM_CreateString(创建一个字符串对象的入口方法)
/* Create a new module string object. The returned string must be freed
* with RedisModule_FreeString(), unless automatic memory is enabled.
*创建一个新的模块字符串对象。必须使用 RedisModule_FreeString() 释放返回的字符串,除非启用了自动内存
* The string is created by copying the `len` bytes starting
* at `ptr`. No reference is retained to the passed buffer.
*该字符串是通过复制从 `ptr` 开始的 `len` 字节来创建的。不保留对传递缓冲区的引用
* The module context 'ctx' is optional and may be NULL if you want to create
* a string out of the context scope. However in that case, the automatic
* memory management will not be available, and the string memory must be
* managed manually.
*模块上下文 'ctx' 是可选的,如果您想在上下文范围之外创建字符串,它可能为 NULL。但是在这种情况下,自动内存管理将不可用,必须手动管理字符串内存
*/
RedisModuleString *RM_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len) {
RedisModuleString *o = createStringObject(ptr,len);
if (ctx != NULL) autoMemoryAdd(ctx,REDISMODULE_AM_STRING,o);
return o;
}
*createStringObject(选择String类型的编码方式)
/* Create a string object with EMBSTR encoding if it is smaller than
* OBJ_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
* used.
*如果小于OBJ_ENCODING_EMBSTR_SIZE_LIMIT(值是44字节的字符串),则创建一个带有EMBSTR编码的字符串对象,否则使用RAW编码
*
* The current limit of 44 is chosen so that the biggest string object
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc.
*选择当前限制 44 以便我们分配为 EMBSTR 的最大字符串对象仍然适合 jemalloc 的 64 字节领域
*/
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(const char *ptr, size_t len) {
if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
从判断可以看出来,是OBJ_ENCODING_EMBSTR_SIZE_LIMIT 的大小决定是按哪种编码方式,目前是44个字节
createRawStringObject(OBJ_ENCODING_RAW 编码方式)
/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
* string object where o->ptr points to a proper sds string.
*创建一个编码为 OBJ_ENCODING_RAW 的字符串对象,这是一个普通的字符串对象,其中 o->ptr 指向一个正确的 sds 字符串
*/
robj *createRawStringObject(const char *ptr, size_t len) {
return createObject(OBJ_STRING, sdsnewlen(ptr,len));
}
//创建新的sds
sds sdsnewlen(const void *init, size_t initlen) {
return _sdsnewlen(init, initlen, 0);
}
//根据type创建存储对象,其中*ptr根据上面的sdsnewlen知道是新的sds
robj *createObject(int type, void *ptr) {
robj *o = zmalloc(sizeof(*o));
o->type = type;
o->encoding = OBJ_ENCODING_RAW;
o->ptr = ptr;
o->refcount = 1;
/* Set the LRU to the current lruclock (minutes resolution), or
* alternatively the LFU counter.
将 LRU 设置为当前的 lruclock(分钟分辨率),或者 LFU 计数器*/
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
}
return o;
}
createEmbeddedStringObject(OBJ_ENCODING_EMBSTR 编码方式)
/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
* an object where the sds string is actually an unmodifiable string
* allocated in the same chunk as the object itself.
*创建一个编码为 OBJ_ENCODING_EMBSTR 的字符串对象,即一个对象,其中 sds 字符串实际上是一个不可修改的字符串,与对象本身分配在同一块中
*/
robj *createEmbeddedStringObject(const char *ptr, size_t len) {
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
struct sdshdr8 *sh = (void*)(o+1);
o->type = OBJ_STRING;
o->encoding = OBJ_ENCODING_EMBSTR;
o->ptr = sh+1;
o->refcount = 1;
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
} else {
o->lru = LRU_CLOCK();
}
sh->len = len;
sh->alloc = len;
sh->flags = SDS_TYPE_8;
if (ptr == SDS_NOINIT)
sh->buf[len] = '\0';
else if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = '\0';
} else {
memset(sh->buf,0,len+1);
}
return o;
}
这里robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1)
将robj和sds放在一个连续的内存块中分配,有利于减少内存碎片
• 16个字节的robj结构。
• 3个字节的sdshdr8头。
• 最多44个字节的sds字符数组。
• 1个NULL结束符。
加起来一共不超过64字节(16+3+44+1),因此这样的一个短字符串可以完全分配在一个64字节长度的内存块中,而且前面创建时对长度也加了限制,44字节,所以不会太长