当前位置: 首页 > 科技观察

为什么Redis不直接使用C字符串,而是自定义简单的动态字符串?

时间:2023-03-22 10:49:17 科技观察

本文转载自微信公众号“编程明珠”,转载请联系编程明珠公众号。作者:守望,linux应用开发者,目前在公众号【编程明珠】上分享Linux/C/C++/数据结构与算法/工具等原创技术文章和学习资源。Redis(一个开源的、基于网络的、基于内存的、可选的持久化键值对存储数据库,用ANSIC编写。)并没有直接使用C语言传统的字符串来表示redis中的字符串,而是使用了一个抽象类型称为简单动态字符串(SDS),并使用SDS作为Redis的默认字符串。那么,为什么要使用这种数据结构而不是传统的字符串呢?让我们先回顾一下C字符串。C语言传统字符串C语言传统字符串是以null结尾的字符数组。例如:charstr[]="你好";计算字符串的长度:strlen(str);C语言中的传统字符串大家应该都比较熟悉,这里就不再继续介绍了。更多相关内容,参考《sizeof,strlen,数组,字符串整在一起的那些坑》和《C语言入坑指南-数组之谜》。简单动态字符串redis中的简单动态字符串定义如下:struct__attribute__((__packed__))sdshdr64{uint64_tlen;//uint64_talloc已被使用;//已分配内存,包括末尾\0unsignedcharflags;//标志位charbuf[];//实际存放字符串的地方};它有很多种,这里选择长度为8字节的进行介绍。看起来很简单是吧?__attribute__((packed))取消了默认的字节对齐方式,这样flags前后不会有潜在的padding字段,也方便网络传输(扩展内容见《理一理字节对齐的那些事》)。len表示buf中存储内容的长度;alloc表示分配的空间。那么,定义这样的SDS有什么好处呢?我们都知道常数复杂度的长度是求得的。strlen获取C传统字符串长度的时间复杂度为O(N)。在上面的结构中,获取字符串长度的时间是复杂度是常数,因为len字段存储的是字符串的长度。虽然这种方法占用的空间多了一点,但是结果是效率上的提升。其实这种做法在很多地方都很常见,比如C++中的标准容器,比如vector获取它的大小,string获取它的长度。预分配空间减少了内存分配的次数。实际上,在新建一个sds的时候,不仅申请了要使用的内存,还申请了一些额外的空间,避免下次修改时需要重新申请内存。这是什么意思?例如,您有一个字符数组:charstr[]="hello";现在你想存储helloworld,你应该怎么做?原来的空间已经确定了,没办法存放那么多字符串,只能重新申请Space,然后把原来的hello复制到新申请的空间中。如果频繁修改字符串,会导致系统频繁申请、释放、复制内存。这样能有高效的redis吗?因此,在redis中,如果出现这种情况,在分配新空间的时候,会预先分配一些空间供下次使用。惰性释放空间,正因为如此,当字符串被缩短时,不需要直接释放内存。您只需要更新字符串并记录当前长度。你说,等下次字符串再长出来的时候,不就再用一次吗?保存二进制数据并查看以下字符串:charstr[]="hello\0world";你说下面的字符串,strle的长度是多少?不是10,也不是11,而是5。为什么?当遇到\0时,计算结束。所以如果要存储一些特殊的字符串,即中间带有\0的字符串,传统的C字符串不好处理。sds不一样,不管你存什么,反正我的长度是记录在len字段里的,我输入多少就记录多少。所以它可以保存二进制数据。扩展请参考《NULL,0,'\0',“0”,"\0"你真的分得清吗?》兼容传统字符串的常用用法。虽然redis新定义了sds这样的结构,但是可以应用于传统C字符串的函数也可以应用于sds。这一点在《数组下标-1你见过吗?》中已经简单提到了。↓lenallocflagbuf因此,类似下面的操作也是安全的:strlen(pSds);/pSdsissdstypestrcasecmp(pSds,"helloworld");//pSdsissdstype所以你现在明白为什么要点了tobuf对吗?适用于传统C字符串的函数也适用于sds。也正因如此,我们看到在源码中,有很多地方sds使用下标-1来访问一些内容:比如sdsIncrLen函数中,voidsdsIncrLen(sdss,ssize_tincr){unsignedcharflags=s[-1];size_tlen;s[-1],相当于*(s-1),下面很容易理解:↓lenallocflagbuf所以下次看到负数下标的时候不要惊讶。小结其实当你了解C++的vector时,你会发现他们使用的思路和预分配常量获取长度和懒释放惊人的相似……本文旨在通过了解vector来学习设计思路和策略sds在redis中的实现。