Redis学习——SDS字符串源码分析
0. 前言
这里对Redis底层字符串的实现分析,但是看完其实现还没有完整的一个概念,即不太清楚作者为什么要这样子设计,只能窥知一点,需要看完redis如何使用再回头来体会,有不足之处还望告知。
涉及文件:sds.h/sds.c
1. 数据结构:
typedef char *sds; struct sdshdr {
unsigned int len; //buf中已使用的字节数
unsigned int free; //buf中未使用的字节数
char buf[]; //缓冲区
};
这里向外提供的api所返回的类型都是sds类型(字符串),这样的话也能够复用一部分的C字符串函数。
这里采用sdshdr结构,存放了字符串长度信息,保证了二进制数据安全,即不仅可以存放字符串,也可用于存放其它二进制数据
2. API实现:
只提取几个API,该文件完整的注释在GitHud上(用户名:jabnih)
a. sdsnewlen
创建一个sds字符串,其它几个创建API都是基于这个API。
创建时采用一次性分配其所需要的空间,即对于buf不进行再次分配,减少了malloc等的调用,同时在释放的时候也减少free次数
//创建一个sds字符串,初始内容为init所指向的内容,buf空间为initlen大小
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh; //这里需要注意
if (init) {
//init不为空,则使用malloc,所申请的空间不会初始化
sh = zmalloc(sizeof(struct sdshdr)+initlen+);
} else {
//init为空,使用calloc,所申请的空间会被初始化为0
sh = zcalloc(sizeof(struct sdshdr)+initlen+);
} if (sh == NULL) return NULL; sh->len = initlen;
sh->free = ;
//这里如果init为NULL,则该buf的内容均为0
if (initlen && init)
memcpy(sh->buf, init, initlen); sh->buf[initlen] = '\0'; return (char*)sh->buf;
}
b. sdsMakeRoomFor
该API的内存分配策略为:在小于SDS_MAX_PREALLOC(即1M)时,会预分配出多一倍的空间,在大于该阈值时,每次只预分配多SDS_MAX_PREALLOC内存。
//保证sds字符串有足够的剩余未使用空间(大于或等于addlen)
sds sdsMakeRoomFor(sds s, size_t addlen) {
struct sdshdr *sh, *newsh;
size_t free = sdsavail(s);
size_t len, newlen; //其剩余的空间满足addlen大小
if (free >= addlen) return s; //不满足addlen大小,需要重新分配
len = sdslen(s);
sh = (void*) (s-(sizeof(struct sdshdr)));
//新空间所需使用的大小为当前sds使用的长度加上addlen
newlen = (len+addlen);
//如果新空间大小比设定的阈值小,则以2倍的增长速度预分配一些空间
if (newlen < SDS_MAX_PREALLOC)
newlen *= ;
else
//比设定阈值大,则只增加PREALLOC预分配大小
newlen += SDS_MAX_PREALLOC;
//重新分配空间
newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+);
if (newsh == NULL) return NULL; newsh->free = newlen - len;
return newsh->buf;
}
c. sdsRemoveFreeSpace
//去除sds字符串中未使用的空间,一般在内存紧张的时候使用
sds sdsRemoveFreeSpace(sds s) {
struct sdshdr *sh; sh = (void*) (s-(sizeof(struct sdshdr)));
sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+);
sh->free = ; return sh->buf;
}
d. sdsclear
//清空sds字符串,但是不释放空间
void sdsclear(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); sh->free += sh->len;
sh->len = ;
sh->buf[] = '\0';
}
3. 总结:
1. 二进制数据安全
2. 预分配空间,可以懒惰释放,在内存紧张的时候也可以缩减不需要的内存
3. 使用该API可以实现内存动态扩展(即不需要考虑内存空间是否足够)
4. 边界检查
Redis学习——SDS字符串源码分析的更多相关文章
- Redis学习——ae事件处理源码分析
0. 前言 Redis在封装事件的处理采用了Reactor模式,添加了定时事件的处理.Redis处理事件是单进程单线程的,而经典Reator模式对事件是串行处理的.即如果有一个事件阻塞过久的话会导致整 ...
- Redis学习之字典源码分析
字典,又叫映射,是一种用于保存键值对的抽象数据结构 划重点:抽象数据结构 Redisd字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表结点,而每个哈希表结点就保存了字典中的一个键值对 一.哈 ...
- Nginx学习笔记4 源码分析
Nginx学习笔记(四) 源码分析 源码分析 在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem).那就来看看这个文件吧!从简单的开始~~~ src/os/unix/Ngx_ ...
- MQTT再学习 -- MQTT 客户端源码分析
MQTT 源码分析,搜索了一下发现网络上讲的很少,多是逍遥子的那几篇. 参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 ...
- 大数据学习--day14(String--StringBuffer--StringBuilder 源码分析、性能比较)
String--StringBuffer--StringBuilder 源码分析.性能比较 站在优秀博客的肩上看问题:https://www.cnblogs.com/dolphin0520/p/377 ...
- 【Redis】事件驱动框架源码分析
aeEventLoop初始化 在server.c文件的initServer函数中,对aeEventLoop进行了初始化: 调用aeCreateEventLoop函数创建aeEventLoop结构体,对 ...
- 【Redis】事件驱动框架源码分析(单线程)
aeEventLoop初始化 在server.c文件的initServer函数中,对aeEventLoop进行了初始化: 调用aeCreateEventLoop函数创建aeEventLoop结构体,对 ...
- Java多线程学习之ThreadLocal源码分析
0.概述 ThreadLocal,即线程本地变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.它可以将变量绑定到特定的线程上,使每个线程都拥有改变量的一个拷贝,各线程相同变量间互不 ...
- springMVC源码学习之addFlashAttribute源码分析
本文主要从falshMap初始化,存,取,消毁来进行源码分析,springmvc版本4.3.18.关于使用及验证请参考另一篇jsp取addFlashAttribute值深入理解即springMVC发r ...
随机推荐
- [转]jsp与servlet的区别联系
原文地址:http://bbs.itheima.com/thread-28972-1-1.html Servlet是Java提供的用于开发Web服务器应用程序的一个组件,运行在服务器端,由Servle ...
- IntelliJ_13书签
一.书签视图 二.使用方法 1.添加书签:Ctrl+Shift+数字 2.跳转到书签:Ctrl+数字 来自为知笔记(Wiz)
- c#学习<二>:数据类型
基元类型 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到Framework类库(FCL)中存在的类型(BCL是FCL的子集). C#中的基元类型 BCL类型 ...
- Ext-设置form表单不可编辑
1. var formEach = win.down('form').items.items; Ext.each(formEach,function(item){ console.log(item); ...
- Echarts-柱状图柱图宽度设置
先看两张图 图中柱图只需要设置series中的坐标系属性barWidth就可以, 这种图柱状图,折叠柱状图都适应 eg: /** * 堆积柱状图 * @param xaxisdata x轴:标签(数组 ...
- 【UVALive 4642】Malfatti Circles(圆,二分)
题 给定三角形,求三个两两相切且与三角形的一条边相切的圆的半径. 二分一个半径,可以得出另外两个半径,需要推一推公式(太久了,我忘记了) #include<cstdio> #include ...
- [cf140e]New Year Garland
Description 用$m$种颜色的彩球装点$n$层的圣诞树.圣诞树的第$i$层恰由$l[i]$个彩球串成一行,且同一层内的相邻彩球颜色不同,同时相邻两层所使用彩球的颜色集合不同. 求有多少种装点 ...
- angularjs 手动加载
利用ng-app可以完成自动加载,如果不利用ng-app.那么使用bootstrarp实现手动加载模块 <html> <head> <script src="a ...
- 微软注册dll在dotnet开发时起到缓存的作用
经过试验,我发觉只要是注册了dll之后,会在全局的环境中得到很好的体现,比如无需指定具体物理路径的dll引用,搜索即可引用等,同时也得到一点: 1.会缓存起这个dll先,在不重启电脑的情况,本地物理路 ...
- POJ2456 Aggressive cows
Aggressive cows 二分,关键是转化为二分! #include <cstdio> #include <algorithm> ; ; int N, C; int a[ ...