先来看看redis中字符串sds的大致结构:
debug所用demo如下
#include "src/server.h"
void testSDS();
void testAlign();
int main(int argc, char **argv) {
// testAlign();
testSDS();
}
/**
* __attribute__ ((__packed__))取消对齐
* __attribute__((aligned(4)))指定对齐大小,这里只整个结构体的对齐,当结构体内各成员间对齐后,没有达到aligned的值,则补齐
* https://code84.com/787223.html
* #pragma pack(8) //设置结构体内成员间对齐大小,pack中参数大小与成员类型最长的值,取小值
* 如:结构体中最大的类型是int四个字节,那么设置pragma pack(8),实际结构体内还是按4字节来对齐,此时,c1一个字节后会补齐三个字节
* 假如c1,c2相邻,这两个成员则合并两个字节,然后补齐两个字节,然后再到下一个成员
*
*/
#pragma pack(2)
typedef struct __attribute__((aligned(8)))/*__attribute__ ((__packed__))*/
{
char c1;//1字节 0 对齐:0
int i; //4字节 1 4
char c2;// 5 8
long l0;
char arr[];
} test_0;
void testSDS() {
sds s0 = sdsnew("ddddddd");//"Xhello sds \n"
sds s00 = sdscat(s0,"catt");//预分配机制,减少分配次数,新增后总长度 len+addlen < 1MB,则按新长度的两倍扩容,新增后总长度 len+addlen > 1MB,则按新长度加上 1MB 扩容
// s = s-1;
size_t size1 = sizeof(s0);
// sdsfree(s00);//释放sds占用的空间
// sdsclear(s00); //只是将字符串的结束标识设置到flag后,将len设置为0
sdsRemoveFreeSpace(s00);//将预分配的空间收缩
//"\001hello sds dddddddddddddddddddddddddddddffffffffffffffffffffffffffdddddddddddddfdfddd"
sds s2 = sdsnew("hello sds dddddddddddddddddddddddddddddffffffffffffffffffffffffffdddddddddddddfdfddd");
sds s22 = sdscat(s2,"catt");
}
void testAlign(){
//内存对齐测试
test_0 a;
test_0 *aa = &a;
printf("c1 -> %d, i -> %d, c2 -> %d,l0-> %d,arr->%d\n",
offsetof(test_0,c1),
offsetof(test_0,i),
offsetof(test_0,c2),
offsetof(test_0,l0),
offsetof(test_0,arr)
);
int size = sizeof(a);
printf("c1 -> %d, i -> %d, c2 -> %d,l0-> %d,a->%d\n",
(unsigned int)(void *)&a.c1 /*- (unsigned int)(void *)&a*/,
(unsigned int)(void *)&a.i/*- (unsigned int)(void *)&a*/,
(unsigned int)(void *)&a.c2 /*- (unsigned int)(void *)&a*/,
(unsigned int)(void *)&a.l0 /*- (unsigned int)(void *)&a*/,
(unsigned int)(void *)&a);
printf("c1 -> %d, i -> %d, c2 -> %d\n",
(unsigned int)(void *)&a.c1 - (unsigned int)(void *)&a,
(unsigned int)(void *)&a.i- (unsigned int)(void *)&a,
(unsigned int)(void *)&a.c2 - (unsigned int)(void *)&a);
//柔性数组测试
test_0 *softp = malloc(sizeof(test_0)+sizeof(int)*4);
test_0 soft = *softp;
int sizes = sizeof(soft);//sizeof不会列出柔性数组占用大小
soft.arr[0] = 'h';
int sizes1 = sizeof(soft);
}
这里我们重点关注几个方法:sdsnew,sdscat,sdsRemoveFreeSpace,sdsfree,设计新建,扩容,收缩,释放。
一、sds创建
redis根据不同长度字符串,使用不同结构体头部尽可能压缩头部占用空间,同时使用__attribute__ ((packed))紧密排列结构体内成员,以节省内存空间。结构体中存储了长度表示,直接根据长度截取数据,避免原始字符串从头开始遍历到结束符的操作。同时SDS也是为兼容C的原始字符串,采用以\0结尾。创建sds的时候返回的不是sdshdr结构体,而是一个char指针,为何要这样骚操作,一个是为了兼容原始字符串,另一个是为了统一处理不同类型的sds。sds不仅可以存储字符串数据,还可以存储二进制数据,如图片,文本等。
二、SDS扩容
在sdshdrX类型不变的扩容时,使用realloc()方法扩容,该方法会尝试在原来的地址上扩展,如果可扩展的连续空间不足,则分配新的空间并复制旧的数据,并释放原来的空间,可考虑自行重新申请内存再把原本的数据复制过去,当然这种方式效率稍低一点点。
三、空间释放
1、sdsclear()
只是将字符串的结束标识设置到flag后,将len设置为0,申请的内存空间并没释放
将s指针往后移一位,仍然可以看到原本的字符串内容:
2、sdsfree()
3、sdsRemoveFreeSpace()