首页 > 编程知识 正文

redis数据结构底层实现(redis list数据结构)

时间:2023-05-03 20:23:41 阅读:92659 作者:196

来源:编程技术精选

作为Redis基础的数据结构非常多,有SDS、ZipList、SkipList、链接列表、HashTable、Intset等。 如果你对Redis的理解还停留在get、set的水平上,就不足以回答面试的问题。 本文简要介绍了Redis最重要的基础数据结构——简单动态序列(SDS )

虽然Redis是用c语言开发的,但是不使用c语言的传统字符串表示(以空字符结尾的字节数组,以下称为c字符串) ) )的抽象类型,单独构建SDS作为Redis的默认字符串表示。

在Redis中,c字符串只用作字符串文字static literal,只用于不需要更改字符串值的地方。 如果Redis不仅需要字符串文字,还需要可更改的字符串值,则Redis将使用SDS来表示字符串值。 例如,在Redis数据库中,包含字符串的键值对由SDS实现。

例如,在客户端执行指令时:

redis set msg '赫勒罗世界'

好的

Redis在数据库中创建新的键-值对。 在这里,如下所示。

键-值对的键是字符串对象,对象的基本实现是存储字符串“msg”的SDS。 键-值对的值也是字符串对象,对象的基本实现是存储字符串“hello world”的SDS。

SDS不仅保存数据库中的字符串值,还用作缓冲区。 AOF模块中的AOF缓冲区和客户端状态的输入缓冲区由SDS实现。 总之,SDS是Redis最基础、最重要的数据结构。

1.SDS的定义

每个sds.h/sdshdr结构都表示一个SDS值。

结构SD硬盘

//记录buf数组中已使用的字节数

等于//SDS中存储的字符串的长度

铟锡兰;

//buf数组的未使用字节数

未完成;

//用于保存字符串的字节数组

卡尔巴赫;

}

用一张图表示:

根据c字符串以空字符结尾的惯例,存储空字符的1字节区域不计算在SDS的len属性中,SDS函数自动进行向空字符分配额外的1字节区域或将空字符添加到字符串末尾的操作,所以该空字符由SDS的使用者

2.SDS和c字符串的区别

目前,c语言使用长度为N 1的字符数组来表示长度为n的字符串,字符数组的最后一个元素始终为空格“”。

这样简单的字符串表示不能满足Redis对字符串的安全性、效率和功能方面的要求。 具体包括以下几个方面。

2.1常数复杂度获取字符串长度

由于c字符串不记录字符串长度的信息,因此为了获取一个c字符串的长度,程序需要遍历整个字符串,对遇到的每个字符进行计数,直到遇到空字符为止。 这个操作的复杂性是o(n )。 另一方面,在Redis的SDS中,这个时间复杂度只有o(1)。

2.2防止缓冲区溢出

align-justify">除了获取字符串长度的复杂度高之外,C字符不记录自身长度带来的另一个问题就是缓冲区溢出。举个例子,C语言的 strcat 函数可以将字符串中的内容拼接到 dest 字符串的末尾,但是当字符串的容量不够就会产生缓存区溢出,因为字符串也是基于数组实现的,也是有大小限制的。

Redis的SDS已经杜绝了这个问题,那它是如何解决的呢?

当API要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的空间,如果不够的话,API会自动将SDS的空间进行扩容,然后才执行实际的修改操作。这就避免了缓冲区内存溢出。

2.3 减少修改字符串时带来的内存重分配次数

上面说到了API会在修改SDS字符串时自动扩容,如果每次修改都伴随着对字符串内的数组的内存重分配,那效率可想而知。所以Redis实现了空间预分配和惰性空间释放两种优化策略。

空间预分配

空间预分配用于优化SDS的字符串增长操作:当SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS分配修改所需要的空间,还会为SDS分配额外的未使用空间。

总的来说,额外分配的未使用空间数量大小有两种可能:

如果对SDS修改之后,SDS的长度将小于1MB,那么程序分配和len 属性同样大小的未使用空间,这时候SDS的 free 属性的值将和 len 属性的值相同。也就是说,该SDS字符串修改完后还有近一半的容量。如果对SDS修改之后,SDS的长度大于等于1MB,那么程序会分配1MB的未使用空间。这个是固定的。

通过空间预分配,Redis可以减少连续执行字符串操作所需的内存重分配次数。

惰性空间释放

惰性空间释放用于优化SDS的字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用 free 属性将这些字节的数量记录起来,并等待将来使用。

2.4 二进制安全

在C语言中,字符串的存储必须符合某种编码(ASCII),并且字符串不能包含空字符,否则会被认为是字符串结尾。这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。

所以,为了解决C字符串的不足,Redis的 buf 数组保存的是二进制数据,这也就是把SDS的 buf 数组称为字节数组的原因。

2.5 兼容部分C字符串函数

虽然 Redis 的API都是二进制安全的,但它们一样遵循C字符串以空字符串结尾的惯例,这些API总会将SDS保存的数据的末尾设置为空字符,并且总会在为 buf 数组分配空间时多分配一个字节来容纳这个空字符,这是为了让那些保存文本数据的SDS可以重用一部分C的函数。

举个例子, 如果我们有一个 SDS 的指针 s , 那么我们可以直接使用 stdio.h/printf 函数, 通过执行以下语句:

printf("%s", s->buf);

来打印出 SDS 保存的字符串值 "Redis" , 而无须为 SDS 编写专门的打印函数。

end:如果你觉得本文对你有帮助的话,记得点赞转发,你的支持就是我更新动力。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。