SDS的使用

SDS在Redis的主要角色是以下两:

实现字符串对象(strinbject);

作为一种替代方案在使用char *类型;

下面两部分分别介绍了这两种用法。

实现字符串对象

Redis是一个键值对数据库(关键值)。数据库的价值可以是各种对象,如字符串、集合、列表等,而数据库的关键始终是字符串对象。

对于包含字符串值的字符串对象,每个字符串对象都包含一个SDS值。

String对象的字符串值,在这个争论的开始听起来有点奇怪,但在redis,字符串对象可以保存字符串值,您还可以保存长型的价值,所以为求严谨,需要强调的是:当字符串对象保存的字符串,它包含是SDS值,否则,它是一个值类型的长。

例如,下面的命令创建一个新的数据库键值对。密钥对的键和值是字符串对象,其中包含一个SDS值。


redis >集书在21天内掌握C++
好啊

redis >这本书
在21天内掌握C++




下面的命令创建另一个键值对,其键是String对象,该值是一个集合对象:


redis >萨德的NoSQLredis mondb Neo4j
(整数)3

redis > smembers NoSQL
1)Neo4j
2) Redis
3)mondb




用SDS替换C的默认char类型

因为char*类型的功能单一、低层次的抽象,并不能有效地支持一些常用的使用操作(如操作和计算长度附加操作),所以在使用程序,绝大多数情况下会使用SDS代替char*来表示字符串。

性能问题将在以后说的定义的SDS,因为我们不了解Redis的其他功能模块,所以尚无详细的方式,例如在使用SDS,但在后面的章节中,我们会经常看到其他模块(几乎每个人)都用在SDS值。

现在,请记住这个事实:在Redis的AOF,协议,缓存的内容,回复客户由客户端发送给客户端,所有这些重要的内容被保存在SDS型。

在Redis的字符串

在C语言中,一个字符串可以用一个以 0结尾的char数组表示。

例如,在C语言中,Hello World可以被表示为Hello World 0。

这种简单的字符串表示在大多数情况下都能满足要求,但不能有效地支持长度计算和追加(追加)两种运算。

每一个计算字符串长度的复杂性(strlen(s))为θ(n)。

一个另外一个字符串必须需要一个N个存储重排(realloc)为一个字符串。

在Redis、添加和长度计算的字符串是很常见的。追加和strlen是两个操作。在Redis命令两个简单的操作,不应成为性能瓶颈。

此外,除了C字符串的使用,还需要处理一个字节数组,和服务器的协议,所以为了方便,Redis的字符串表示形式也应该是二进制安全的程序不应该做任何内部存储的数据串的假设,数据可以C◎0在结尾的字符串,也可以是一个简单的字节数组,或其他格式的数据。

以这两个原因,Redis使用SDS型取代C语言的默认字符串表示:SDS可以有效地增加计算长度,是二进制安全的。

实施SDS

在前面的内容中,我们将SDS描述为一个抽象的数据结构。事实上,它的实现由以下两部分组成:


typedef char * SDS;


结构sdshdr {

/ /缓冲区占用的长度
Int len;

剩余的可用长度 / /缓冲
国际自由;

本地保存字符串的实际数据
中字符{};
};




其中,式表的别名(别名)char *,和结构sdshdr保留晶状体,免费的三属性,和缓冲区。

作为一个例子,下面是新创建的sdshdr结构,也节省了Hello World字符串:


结构sdshdr {
= 11;
自由= 0;
buf =hello world 0 ; / /缓冲区的实际长度为len + 1
};



通过透镜的属性,sdshdr可以随着θ的复杂性达到长度计算操作(1)。

另一方面,通过分配额外的空间来缓冲和免费使用记录的未使用的空间的大小,sdshdr可以减少需要执行额外的操作,内存分配时间。我们将在下一节中详细讨论这一点。

当然,SDS也需要正确的操作执行。这个过程sdshdr必须更新Len和自由正确的属性的所有功能,否则会导致错误。

数据类型定义

与SDS实现有关的数据类型有两种,一种是SDS:


字符串类型别名
typedef char * SDS;






另一个是sdshdr:


保持结构
结构sdshdr {
字符串 / /空间数量已在buf
Int len;
一些预留空间 / /缓冲区的字符串
国际自由;
实际存储位置字符串
中字符{};
};






其中,SDS是字符串数组char*类型的别名,和sdshdr用于保存和保存信息的SDS

例如,sdshdr.len可以存储复杂度为O sdshdr.buf下字符串的实际长度(1),而sdshdr.free用于保存在sdshdr.buf更预留空间。

(sdshdr应该简称SDS处理)

使用sdshdr为SDS

SDS模块使用一个小技巧sdshdr结构:通过指针操作,它使sdshdr结构来传输和处理如SDS和恢复sdshdr型时所需要的类型。

通过下面的函数定义理解这个技术

的sdsnewlen函数返回一个新的SDS值。事实上,它创造了一个sdshdr结构:


SDS sdsnewlen(const void * init,size_t initlen)
{
结构sdshdr * SH;

如果(init){
创建
SH = malloc(sizeof(struct sdshdr)+ initlen + 1);
{人}
/重新分配
SH = calloc(1,sizeof(struct sdshdr)+ initlen + 1);
}

是否(返回空)返回null;

上海-> len = initlen;
SH =自由= 0;第一个自由0

如果(initlen init){
Memcpy(上海-> buf,init,initlen);
}
上海-> buf { initlen } = 0;

SH -> buf / /只返回字符串部分
返回(char *)SH -> buf;
}






通过使用变量值SDS,可以直接通过SDS他们当你遇到一个只处理SDS值本身的功能。例如,在sdstoupper函数就是一个例子:




静态内联size_t sdslen(const SDS的)
{
/ /计算相应的sdshdr结构从SDS
结构sdshdr * SH =(void *)(S(sizeof(struct sdshdr)));

返回;
}


无效sdstoupper(SDS的)
{
int len = sdslen(S)、J;

对于(j = 0;j;<;j + +)
{ } =直接的J(J的{ });
}





有一个诀窍来从SDS值通过指针操作计算相应的sdshdr结构。

虽然SDS是指向char * buf(PS:和一个空数组不占用内存空间,数组名是内存地址),但是当分配sizeof(struct分布sdshdr)+ initlen + 1,SDS - sizeof(struct sdshdr)可以计算结构sdshdr第一地址,可得到Len和免费的信息



的sdsavail功能是使用这个技术的一个例子。




静态内联size_t sdsavail(const SDS的)
{
结构sdshdr * SH =(void *)(S(sizeof(struct sdshdr)));

返回免费;
}





内存分配功能的实现

Reids的决策实现相关功能sdsmakeroomfor:




SDS sdsmakeroomfor(SDS,size_t addlen)
{
结构sdshdr * SH * newsh;
size_t自由= sdsavail(S);
size_t Len,newlen;

保留空间可以满足本地马赛克。
如果返回的(自由> = addlen);

len = sdslen(S);
SH =(void *)(S(sizeof(struct sdshdr)));

建立一个新的SDS /字符串长度
这个长度比完成拼接实际长度所需的长度要大。
通过对下一步空间拼接操作的优化
newlen =(长+ addlen);
如果(newlen<1024×1024)
newlen * = 2;
其他的
newlen = 1024;

sdshdr / /重新分配
newsh = realloc(SH,sizeof(struct sdshdr)+ newlen + 1);
如果(newsh = = null)返回null;

newsh ->自由= newlen -莱恩;

只返回字符串的一部分
返回newsh -> buf;
}





这种内存分配策略表明:膨胀时SDS值(扩大),总是会有预留额外的空间,这将减少再分配的数量(分配)和支出更多的内存优化的下一个扩展操作的处理速度。

把redis为SDS字符串扩展是一个好主意。



*延长SDS的长度,镜头和剪接T的SDS结束。
* /
SDS sdscatlen(SDS,const void * T,size_t Len)
{
结构sdshdr * SH;

size_t curlen = sdslen(S);

o(n)
S = sdsmakeroomfor(S,Len);
如果(NULL = NULL)返回null;

复制
Memcpy(S + curlen,T,Len);

更新镜头和免费属性
SH =(void *)(S(sizeof(struct sdshdr)));
上海-> len = curlen +透镜;
自由;

{终端}
{ }的curlen + len = 0;

返回的;
}


*将一个字符数组连接到SDS末端
* /
SDS sdscat(SDS,const char * T)
{
返回sdscatlen(S,T,strlen(t));
}