2024年SDS——Redis源码剖析(1),docker入门书籍

本文介绍了面试准备策略,建议按专题学习Java基础、算法、数据库等内容,并推荐了一份包含Java面试题解析、学习笔记和视频的系统资料。作者分享了自己的学习经验,强调了持续学习和实践的重要性,对于希望成为优秀架构师的人有很大帮助。
摘要由CSDN通过智能技术生成

最后

关于面试刷题也是有方法可言的,建议最好是按照专题来进行,然后由基础到高级,由浅入深来,效果会更好。当然,这些内容我也全部整理在一份pdf文档内,分成了以下几大专题:

  • Java基础部分

  • 算法与编程

  • 数据库部分

  • 流行的框架与新技术(Spring+SpringCloud+SpringCloudAlibaba)

这份面试文档当然不止这些内容,实际上像JVM、设计模式、ZK、MQ、数据结构等其他部分的面试内容均有涉及,因为文章篇幅,就不全部在这里阐述了。

作为一名程序员,阶段性的学习是必不可少的,而且需要保持一定的持续性,这次在这个阶段内,我对一些重点的知识点进行了系统的复习,一方面巩固了自己的基础,另一方面也提升了自己的知识广度和深度。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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] = ‘\0’;

return s;

}

SDS的使用


上面代码中我特意标注了一个注意sdsnewlen()返回的sds指针并不是直接指向sdshdr的地址,而是直接指向了sdshdr中buf的地址。这样做有啥好处?好处就是这样可以兼容c原生字符串。buf其实就是C 原生字符串+部分空余空间,中间是特殊符号’\0’隔开,‘\0’有是标识C字符串末尾的符号,这样就实现了和C原生字符串的兼容,部分C字符串的API也就可以直接使用了。 当然这也有坏处,这样就没法直接拿到len和alloc的具体值了,但是也不是没有办法。

当我们拿到一个sds,假设这个sds就叫s吧,其实一开始我们对这个sds一无所知,连他是sdshdr几都不知道,这时候可以看下s的前面一个字节,我们已经知道sdshdr的数据结构了,前一个字节就是flag,根据flag具体的值我们就可以推断出s具体是哪个sdshdr,也可以推断出sds的真正地址,相应的就知道了它的len和alloc,知道了这点,下面这些有点晦涩的代码就很好理解了。

oldtype = s[-1] & SDS_TYPE_MASK; // SDS_TYPE_MASK = 7 看下s前面一个字节(flag)推算出sdshdr的类型。

// 这个宏定义直接推算出sdshdr头部的内存地址

#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))

#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)

// 获取sds支持的长度

static inline size_t sdslen(const sds s) {

unsigned char flags = s[-1]; // -1 相当于获取到了sdshdr中的flag字段

switch(flags&SDS_TYPE_MASK) {

case SDS_TYPE_5:

return SDS_TYPE_5_LEN(flags);

case SDS_TYPE_8:

return SDS_HDR(8,s)->len; // 宏替换获取到sdshdr中的len

// 省略 SDS_TYPE_16 SDS_TYPE_32的代码……

case SDS_TYPE_64:

return SDS_HDR(64,s)->len;

}

return 0;

}

// 获取sds剩余可用空间大小

static inline size_t sdsavail(const sds s) {

unsigned char flags = s[-1];

switch(flags&SDS_TYPE_MASK) {

case SDS_TYPE_5: {

return 0;

}

case SDS_TYPE_8: {

SDS_HDR_VAR(8,s);

return sh->alloc - sh->len;

}

// 省略 SDS_TYPE_16 SDS_TYPE_32的代码……

case SDS_TYPE_64: {

SDS_HDR_VAR(64,s);

return sh->alloc - sh->len;

}

}

return 0;

}

/* 返回sds实际的起始位置指针 */

void *sdsAllocPtr(sds s) {

return (void*) (s-sdsHdrSize(s[-1]));

}

SDS的扩容


在做字符串拼接的时候,sds可能剩余的可用空间不足,这个时候需要扩容,什么时候该扩容,又该怎么扩? 这是不得不考虑的问题。Java中很多数据结构都有动态扩容的机制,比如和sds很类似的StringBuffer,HashMap,他们都会在使用过程中动态判断是否空间充足,而且基本上都采用了先指数扩容,然后到一定大小限制后才开始线性扩容的方式,Redis也不例外,Redis在1024_1024以内都是2倍的方式扩容,只要不超出1024_1024都是先额外申请200%的空间,但一旦总长度超过1024_1024字节,那每次最多只会扩容1024_1024字节。 Redis中sds扩容的代码是在sdsMakeRoomFor(),可以看到很多字符串变更的API开头都直接或者间接调用这个。 和Java中StringBuffer扩容不同的是,Redis这里还需要考虑不同字符串长度时sdshdr类型的变化,具体代码如下:

// 扩大sds的实际可用空间,以便后续能拼接更多字符串。

// 注意:这里实际不会改变sds的长度,只是增加了更多可用的空间(buf)

sds sdsMakeRoomFor(sds s, size_t addlen) {

void *sh, *newsh;

size_t avail = sdsavail(s);

size_t len, newlen;

char type, oldtype = s[-1] & SDS_TYPE_MASK; // SDS_TYPE_MASK = 7

int hdrlen;

/* 如果有足够的剩余空间,直接返回 */

if (avail >= addlen) return s;

len = sdslen(s);

sh = (char*)s-sdsHdrSize(oldtype);

newlen = (len+addlen);

// 在未超出SDS_MAX_PREALLOC前,扩容都是按2倍的方式扩容,超出后只能递增

if (newlen < SDS_MAX_PREALLOC) // SDS_MAX_PREALLOC = 1024*1024

newlen *= 2;

else

newlen += SDS_MAX_PREALLOC;

type = sdsReqType(newlen);

/* 在真正使用过程中不会用到type5,如果遇到type5直接使用type8*/

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) return NULL;

s = (char*)newsh+hdrlen;

} else {

// 扩容其实就是申请新的空间,然后把旧数据挪过去

newsh = s_malloc(hdrlen+newlen+1);

if (newsh == NULL) return NULL;

memcpy((char*)newsh+hdrlen, s, len+1);

s_free(sh);

s = (char*)newsh+hdrlen;

s[-1] = type;

sdssetlen(s, len);

}

sdssetalloc(s, newlen);

return s;

}

常用API


sds.c还有很多源码我都没有贴到,其他代码本质上都是围绕sdshdr数据结构和各种字符串操作写的(基本上都是各种字符串新建、拼接、拷贝、扩容……),只要知道了sds的设计原理,相信你也能轻易写出来,这里我就列一下所有sds相关的API,对源码有兴趣的旁友可以移步到src/sds.c,中文注释版的API列表见src/sds.c

sds sdsnewlen(const void *init, size_t initlen); // 新建一个容量为initlen的sds

sds sdsnew(const char *init); // 新建sds,字符串为null,默认长度0

sds sdsempty(void); // 新建空字符“”

sds sdsdup(const sds s); // 根据s的实际长度创建新的sds,目的是降低内存的占用

void sdsfree(sds s); // 释放sds

sds sdsgrowzero(sds s, size_t len); // 把sds增长到指定的长度,增长出来的新的空间用0填充

sds sdscatlen(sds s, const void *t, size_t len); // 在sds上拼接字符串t的指定长度部分

sds sdscat(sds s, const char *t); // 把字符串t拼接到sds上

sds sdscatsds(sds s, const sds t); // 把两个sds拼接在一起

sds sdscpylen(sds s, const char *t, size_t len); // 把字符串t指定长度的部分拷贝到sds上

sds sdscpy(sds s, const char *t); // 把字符串t拷贝到sds上

sds sdscatvprintf(sds s, const char *fmt, va_list ap); // 把用printf格式化后的字符拼接到sds上

sds sdscatfmt(sds s, char const *fmt, …); // 将多个参数格式化成一个字符串后拼接到sds上

sds sdstrim(sds s, const char *cset); // 在sds中移除开头或者末尾在cset中的字符

void sdsrange(sds s, ssize_t start, ssize_t end); // 截取sds的子串

void sdsupdatelen(sds s); // 更新sds字符串的长度

void sdsclear(sds s); // 清空sds中的内容,但不释放空间

int sdscmp(const sds s1, const sds s2); // sds字符串比较大小

sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);

void sdsfreesplitres(sds *tokens, int count);

void sdstolower(sds s); // 字符串转小写

void sdstoupper(sds s); // 字符串转大写

sds sdsfromlonglong(long long value); // 把一个long long型的数转成sds

sds sdscatrepr(sds s, const char *p, size_t len);

sds *sdssplitargs(const char *line, int *argc);

总结

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

[外链图片转存中…(img-QA2ZRKgq-1715005775459)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 11
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值