SDS的使用上面代码中我特意标注了一个注意 , sdsnewlen()返回的sds指针并不是直接指向sdshdr的地址 , 而是直接指向了sdshdr中buf的地址 。这样做有啥好处?好处就是这样可以兼容c原生字符串 。buf其实就是C 原生字符串+部分空余空间 , 中间是特殊符号''隔开 , ‘’有是标识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
推荐阅读
- 看了两天HashMap源码,终于把红黑树插入平衡规则搞懂了
- 手写Redis分布式锁
- 这份源码阅读步骤你值得拥有
- RediSearch 2.0 GA 发布,高性能全文搜索引擎
- 太厉害了!这应该是目前Redis可视化工具最全的横向评测
- Redis 密码设置和查看密码
- 新闻个性化推荐系统源码之构建离线用户画像
- 利用AOP自定义Redis缓存注解
- 监控Redis?使用Grafana的Source插件轻松搞定
- 今日给大家推荐两款redis可视化工具
