2)发现说明
【1】为什么要对原本的数据结构进行修改?(改版后的优化在哪里)
因为int占据4个字节(8bit) , 也就是能存42亿左右的 , 但是在我们实际上 , 存储的数据大概率都是小数据 , 所以它存在浪费资源的嫌疑 。
所以进行优化的思维就是根据不同的数据范围 , 设置不同容量 , 如 , uint8_t 表示占据1字节(8bit , 在二进制中最大可以表示255) , uint16_t 表示占据2字节(16bit , 在二进制中最大可以表示65535)
【2】官网上说String类型限制大小512M , 是怎么限制的?
//位于t_string.c文件中//为什么要限制 , 要知道512M已经是一个很大的值了(已经是一个bigkey了) , 在redis单线程操作中已经很容易阻塞线程//故在追加命令appendCommand和设置命令setrangeCommand中都会进行校验static int checkStringLength(client *c, long long size) {if (size > 512*1024*1024) {addReplyError(c,"string exceeds maximum allowed size (512MB)");return C_ERR;}return C_OK;}3)分析是怎么创建的
//在sds.c文件内//sds在创建的时候 , buf数组初始大小为:struct结构体大小 + 字符串的长度+1, +1是为了在字符串末尾添加一个\0 。//在完成字符串到字符数组的拷贝之后 , 会在字符串末尾加一个\0 , 这样可以复用C语言的一些函数 。sds sdsnewlen(const void *init, size_t initlen) {void *sh;sds s;// 根据长度计算sds类型char type = sdsReqType(initlen);if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;//为空时强制用sdshdr8// 获取结构体大小int hdrlen = sdsHdrSize(type);unsigned char *fp; /* flags pointer. */// 分配内存空间 , 初始大小为:struct结构体大小+字符串的长度+1,+1是为了在字符串末尾添加一个\0 , 兼容传统C语言sh = s_malloc(hdrlen+initlen+1);// sh在这里指向了这个刚刚分配的内存地址if (sh == NULL) return NULL;// 判断是否是init阶段if (!init)//init 不为空的话 , 将sh这块内存全部设置为0memset(sh, 0, hdrlen+initlen+1);// 指向buf数组的指针s = (char*)sh+hdrlen;//因为可以看到地址的顺序是 len,alloc,flag,buf,目前s是指向buf , 那么后退1位,fp 正好指向了flag对应的地址fp = ((unsigned char*)s)-1;// 类型选择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;}}//如果两者都不为空 , 则init 这个对应的字符串 , 赋值给sif (initlen && init)memcpy(s, init, initlen); // 将字符串拷贝到buf数组s[initlen] = '\0';// 字符串末尾添加一个\0return s;}// 获取结构体大小static inline int sdsHdrSize(char type) {switch(type&SDS_TYPE_MASK) {case SDS_TYPE_5:return sizeof(struct sdshdr5);case SDS_TYPE_8:return sizeof(struct sdshdr8);case SDS_TYPE_16:return sizeof(struct sdshdr16);case SDS_TYPE_32:return sizeof(struct sdshdr32);case SDS_TYPE_64:return sizeof(struct sdshdr64);}return 0;}4)怎么防止操作时缓冲区溢出
//先检查 SDS 的空间是否满足修改所需的要求//如果不满足要求的话 , API 会自动将 SDS 的空间扩展到执行修改所需的大小//最后才是返回 , 去执行实际的修改操作sds sdscatlen(sds s, const void *t, size_t len) {size_t curlen = sdslen(s);//获取s已经使用过的空间字符数s = sdsMakeRoomFor(s,len);//扩大s的空闲空间if (s == NULL) return NULL;memcpy(s+curlen, t, len);//拷贝数据sdssetlen(s, curlen+len);//设置s的lens[curlen+len] = '\0'; //最后加上空字符串return s;}
经验总结扩展阅读
- 七 Netty 学习:NioEventLoop 对应线程的创建和启动源码说明
- Spring mvc源码分析系列--Servlet的前世今生
- spring cron表达式源码分析
- 集合框架——LinkedList集合源码分析
- 含源码 手把手教你使用LabVIEW OpenCV DNN实现手写数字识别
- 补充部分---ScheduledThreadPoolExecutor类分析 线程池底层原理详解与源码分析
- 五 Netty 学习:服务端启动核心流程源码说明
- HashMap底层原理及jdk1.8源码解读
- 含源码 手把手教你使用LabVIEW人工智能视觉工具包快速实现传统Opencv算子的调用
- 含源码 手把手教你使用LabVIEW人工智能视觉工具包快速实现图像读取与采集