redis源码分析(1)——简单字符串的实现

本系列所有文章基于《Redis设计与实现》学习而做的随笔,本文使用的redis源码为3.0版本,以后不再赘述

c语言的字符串

首先,我们都知道,Redis是用c语言实现的,而c语言,有个很大的弊端,那就是没有原生的字符串,字符串,是使用字符数组实现的。
基于这个原因,我们可以分析一下c字符串的缺点。
1. 字符串的拼接需要提前判断空间是否足够,否则可能造成缓冲区溢出。此外还必须进行malloc分配内存占用大量系统资源。
2. 字符串缩短时需要释放空间,否则可能造成内存泄漏。
那么,Redis的第一个要解决的就是实现一个高效的字符串

Redis的SDS

简单动态字符串(simple dynamic string,SDS)的定义如下:
sds.h

struct sdshdr{
    int len; //记录buf中字符串的长度(strlen)

    int free; //记录buf中未使用的空间大小;

    char buf[];  //保存字符串的数组
}

举个例子:

buf 申请了10sizeof(char)的空间,保存的内容是"Redis"
那么,len = 5;
free = 4;
*剩余的那个空间是用来存储'\0'的。

SDS的好处

  1. sds遵从了c字符串以’\0’结尾的惯例,好处是可以直接使用c字符串的一些库函数,如printf("s%",s->buf);
  2. 计算sds长度无需再使用strlen(),strlen复杂度是O(n)的,而sds则比较容易,直接读取sds->len,时间复杂度为O(1)。
  3. 杜绝缓冲区溢出。sds操作时,会自动检测原始空间是否满足,如果不满足会重新分配内存。
  4. 减少修改字符串时重新分配内存的次数。
  5. 二进制安全

减少内存重分配次数

  • 如果此次进行的操作会加长字符串,那么会判断空间是否足够,不足够才会重新分配。
  • 如果此次操作会减短字符串,那么不会直接释放内存,而是改变free的大小。

具体实现

空间预分配

假如普通的c字符串,一般来说每次strcat()之前都要重新分配内存。
sds会先去检测是否足够,不足则重分,但这实际上的次数几乎达到n次。
redis使用以下策略大大降低了重分配的次数:

//分别表示free,len,buf
sds1={0,5,"Redis"}
sds2={0,5,"Hello"}

现在要将sds1连接到sds2之后。
那么首先,判断剩余空间是否足够,明显不足,分配内存到可以存下。
策略如下,如果分配内存后大小小于1M,则多分配和len大小相同同的空间。

sds2={010"Hello"}
sds2={10,10,"Hello"} //最终的分配

如果大于1M,则多分配1M的free空间。
如此,最终的结果为

sds2={10,10,"HelloRedis"}

这种策略,将内存分配的次数从n次变成最多n次

惰性空间释放
sds={3,7,"AAbcbAA"}
sdstrim(sds,"Ac") //移除sds中所有的A和c字符
结果为:
sds={8,2,"bb"}

缩短sds,不直接释放空间,仅仅修改len和free,多余的free便于下次曾长sds时使用。

二进制安全

我们知道,c语言是以'/0'来作为字符的结尾,那么如果有这样一个字符串"abc\0de\0",c只会识别到abc\0。这使得存储二进制内容无法实现(二进制内容或许会存在很多'\0')。
sds是二进制安全的,原因在于其使用len成员来确定内容长度而非'\0'

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值