redis源码分析-sds字符串
来源:互联网 发布:模拟别人说话声音软件 编辑:程序博客网 时间:2024/04/28 17:56
1.简介
在c语言中,一般使用char定义字符串类型,而redis却不一样,采用sds结构进行存储。那么为什么redis弃用char而改用sds呢?这样做是基于哪些方面的考虑?这样做的优缺点各有哪些呢?
2.SDS结构
带着上面的疑问,我们先回到原点,看下sds的数据结构(以3.0版本为例)(sds结构及相关操作代码位于src/sds.h、src/sds.c文件)。
struct sdshdr { int len; int free; char buf[]; };
从上面的结构可以看出,sds字符串比c字符串多2个属性,占用的字节数比c字符串多 4+4+1(sds)-1(char) = 8个。
- len: 已占用空间长度
- free:剩余空间长度
- buf: 字符串数据
下面通过简单的实例讲解sds字符串创建、追加、释放操作,以便加深对sds结构的理解。
a).sds“创建”操作:定义一个“hello”的str字符串,如下:
sds sdsnewlen(const void *init, size_t initlen) { struct sdshdr *sh; //判断是否有指定内容,如果有,则不重置分配内存的内容。如果没有,则填充0 if (init) { sh = zmalloc(sizeof(struct sdshdr)+initlen+1); } else { sh = zcalloc(sizeof(struct sdshdr)+initlen+1); } if (sh == NULL) return NULL; //指定字符串长度 sh->len = initlen; //剩余空间默认为0 sh->free = 0; if (initlen && init) //将字符串填充至buf memcpy(sh->buf, init, initlen); // 以 \0 结尾 sh->buf[initlen] = '\0'; return (char*)sh->buf;}
str的SDS结构图:
b).sds追加:当我们将“ world”和“!”分别追加至str字符串,redis会执行以下函数进行处理:
sds sdscat(sds s, const char *t) { return sdscatlen(s, t, strlen(t));}//拼接字符串sds sdscatlen(sds s, const void *t, size_t len) { struct sdshdr *sh; // 原有字符串长度 size_t curlen = sdslen(s); // 扩展 sds 空间 s = sdsMakeRoomFor(s,len); // 申请空间失败 if (s == NULL) return NULL; //将新字符串copy至字符串尾部 sh = (void*) (s-(sizeof(struct sdshdr))); memcpy(s+curlen, t, len); // 更新属性 sh->len = curlen+len; sh->free = sh->free-len; // 添加新结尾符号 s[curlen+len] = '\0'; return s;}//扩展空间sds sdsMakeRoomFor(sds s, size_t addlen) { struct sdshdr *sh, *newsh; // 获取 s 目前的剩余空间长度 size_t free = sdsavail(s); size_t len, newlen; //如果free>=addlen 则无需申请空间。 if (free >= addlen) return s; len = sdslen(s); sh = (void*) (s-(sizeof(struct sdshdr))); //新字符串长度 newlen = (len+addlen); //如果newlen<SDS_MAX_PREALLOC时,按照2倍*新字符串长度进行分配空间 //否则,申请的空间=新字符串长度+SDS_MAX_PREALLOC //SDS_MAX_PREALLOC 默认为1024*1024 (1M) if (newlen < SDS_MAX_PREALLOC) newlen *= 2; else newlen += SDS_MAX_PREALLOC; newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1); //申请空间失败,返回null if (newsh == NULL) return NULL; //重置新字符串的剩余空间属性 newsh->free = newlen - len; // 返回 sds return newsh->buf;}
追加“ world”后str的SDS结构图:
此时str->free=11,当再次追加“!”至str时,系统无需重新分配空间,只须将新字符串copy至尾部即可。SDS结构图如下:
c).sds释放空间操作,代码如下:
void sdsfree(sds s) { if (s == NULL) return; zfree(s-sizeof(struct sdshdr));}
3.SDS与C字符串区别
a).sds占用空间比c字符串多8个字节4(len)+4(free);
b).相比C字符串,sds计算长度时间复杂度降低了很多,前者O(n),后者O(1).
c).减少内存分配次数:内存分配是一个费时费力的“工程”。当redis作为数据库时,数据变更会经常发生。使用SDS保存字符串数据能够有效地减少内存分配次数。
d).二进制安全:我们都知道C字符串的内容不能包含空字符,否则最先被程序读入的空字符会被误认为字符串的结束。而这一限制使得C字符串只能保存纯文本数据,无法保存像图片、视频、压缩文件等二进制文件。而sds字符串不一样,字符串长度是根据len属性来决定的,即使内容中含有空字符串,则对数据的完整性也没有任何影响。
4.兼容C字符串函数
细心的读者可以发现,sds的buf与c字符串一样,在字符串的末尾增加空字符串来表示结束。这样就可以保证sds字符串沿用C语言字符串的部分函数,而无需进行重写。
5.总结
a).sds字符串采用以“空间换时间”的做法达到提升性能的目的。
b).sds字符串功能更为强大,能支持多种格式存储数据。
- redis源码分析-sds字符串
- 【redis源码分析】动态字符串--sds
- Redis源码分析(四)-- sds字符串
- Redis源码分析(一)——Redis数据结构-字符串SDS
- redis源码分析之简单动态字符串sds
- redis源码分析(二)-sds字符串的实现
- redis源码分析(二)、sds动态字符串学习总结
- Redis源码分析(sds)
- redis源码分析(二)、redis源码分析之sds字符串
- Redis源码-数据结构之sds字符串
- redis源码学习-sds字符串结构
- Redis源码剖析--动态字符串SDS
- Redis源码剖析--简单动态字符串sds
- Redis源码剖析--简单动态字符串sds
- redis源码分析笔记2- redis的数据类型-动态字符串sds
- redis源码分析(3)sds
- Redis源码分析——SDS
- Redis源码分析(五)——简单动态字符串(sds)
- RecycleView 仿支付宝实现item拖动效果
- Irrlicht学习笔记(7.2)--Collision
- MySQL 触发器简单实例
- 类集框架List
- swiper的基础使用(十五)
- redis源码分析-sds字符串
- Hibernate工作机制
- C++的IO库操作易错
- swiper的基础使用(十六)
- python sorted()函数用法
- 栈的压入、弹出序列
- GRACE学习系列(二)
- swiper的基础使用(十七)
- 多线程-join的应用