Redis源码阅读一:简单动态字符串SDS
Posted pusidun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis源码阅读一:简单动态字符串SDS相关的知识,希望对你有一定的参考价值。
源码阅读基于Redis4.0.9
SDS介绍
redis 127.0.0.1:6379> SET dbname redis
OK
redis 127.0.0.1:6379> GET dbname
"redis"
从上面的例子可以看到,key为dbname的值是一个字符串“redis”
Redis源码是用c写成,但并没有使用c的字符串。c的字符串有以下缺点:
- 没有储存字符串长度的变量,获取长度只能靠遍历字符串
- 扩容麻烦。没有相应保护,容易造成缓冲区溢出
- 更新字符串需要重新分配内存
addr | value |
---|---|
0x0 | s |
0x1 | t |
0x2 | r |
0x3 | 1 |
0x4 | ‘ ‘ |
0x5 | |
0x6 | |
0x7 | |
0x8 | a |
0x9 | b |
0xa | ‘ ‘ |
解释下2,3点。上图是一段连续的内存,保存了字符串"str1"和“ab”。如果我们用strcat函数,拼接一个“append”在“str1”后面,就会对“ab”产生影响。造成内存的破坏。
同样的道理,想要更新字符串,同时又不造成溢出,只能重新分配一段内存。
普通的应用程序,上面的操作是可以接受的。但是redis作为数据库,经常增删改查,加上对速度有一定需求,所以不能使用C的字符串。
我们可以在src/sds.h中找到sds的声明:
typedef char *sds;
怎么回事?redis中的sds还是char* ,那不是和C字符串一样了吗?
其实这里只是为了兼容,而每个sds字符串前都有一个sds header,保存了该sds字符串的信息
下面是sdsnew函数,用来创建一个sds字符串
/* Create a new sds string starting from a null terminated C string. */
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
/* for example mystring = sdsnewlen("abc",3); */
sds sdsnewlen(const void *init, size_t initlen) {
void *sh;
sds s;
char type = sdsReqType(initlen); //根据initlen的值计算出type类型
/* Empty strings are usually created in order to append. Use type 8
* since type 5 is not good at this. */
if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
int hdrlen = sdsHdrSize(type);
unsigned char *fp; /* flags pointer. */
/*给sds header分配空间*/
sh = s_malloc(hdrlen+initlen+1);
if (!init)
memset(sh, 0, hdrlen+initlen+1);
if (sh == NULL) return NULL;
s = (char*)sh+hdrlen;
fp = ((unsigned char*)s)-1;
/*根据type初始化sh的成员*/
switch(type) {
case SDS_TYPE_5: {
*fp = type | (initlen << SDS_TYPE_BITS);
break;
}
case SDS_TYPE_8: {
SDS_HDR_VAR(8,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_16: {
SDS_HDR_VAR(16,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_32: {
SDS_HDR_VAR(32,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
case SDS_TYPE_64: {
SDS_HDR_VAR(64,s);
sh->len = initlen;
sh->alloc = initlen;
*fp = type;
break;
}
}
if (initlen && init)
memcpy(s, init, initlen);
s[initlen] = '