/* Create an empty (zero length) sds string. Even in this case the string * always has an implicit null term. */ sds sdsempty(void) { return sdsnewlen("", 0); }
/* Create a new sds string starting from a null terminated C string. */ sds sdsnew(constchar *init) { size_t initlen = (init == NULL) ? 0 : strlen(init); return sdsnewlen(init, initlen); }
/* Free an sds string. No operation is performed if 's' is NULL. */ voidsdsfree(sds s) { if (s == NULL) return; s_free((char *) s - sdsHdrSize(s[-1])); }
/* Don't use type 5: the user is appending to the string and type 5 is * not able to remember empty space, so sdsMakeRoomFor() must be called * at every appending operation. */ // 不要使用 SDS5,追加会重新内存分配,不友好 if (type == SDS_TYPE_5) type = SDS_TYPE_8;
// 新类型对应的头长度 hdrlen = sdsHdrSize(type);
// 不需要改变类型 if (oldtype==type) { newsh = s_realloc(sh, hdrlen+newlen+1); if (newsh == NULL) returnNULL; s = (char*)newsh+hdrlen; } else { /* Since the header size changes, need to move the string forward, * and can't use realloc */ newsh = s_malloc(hdrlen+newlen+1); if (newsh == NULL) returnNULL; memcpy((char*)newsh+hdrlen, s, len+1); s_free(sh); s = (char*)newsh+hdrlen; s[-1] = type; sdssetlen(s, len); }
127.0.0.1:6379> set name zhangsan OK 127.0.0.1:6379> object encoding name "embstr" 127.0.0.1:6379> set address zheshiyigechaoguosishisiweidezifuchuandaibiaodeshizhangsandedizhi OK 127.0.0.1:6379> object encoding address "raw"
cpu cache line 的大小通常是 64B,所以如果字符串长度不大于 44 字节时,redis 会将字符串与 RedisObject 紧密存储,这样 cpu 在读取数据时无需再次访问内存,充分利用 cpu cache。
但是注意,如果对某个字符串进行了 append 等操作,redis 会将其修改为 raw 类型,即使依然不超 44 字节。
1 2 3 4 5 6 7 8 9 10
127.0.0.1:6379> append name feng (integer) 12 127.0.0.1:6379> get name "zhangsanfeng" 127.0.0.1:6379> object encoding name "raw" 127.0.0.1:6379> set name2 zhangsanfeng OK 127.0.0.1:6379> object encoding name2 "embstr"
小结
Redis 摒弃 C 语言原生字符串,使用 SDS 简单动态字符串,核心是用带元信息的结构体替代纯 char*,完美解决传统字符串二进制不安全、获取长度 / 拼接操作效率低的痛点。