Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis 动态字符串SDS

Redis简单动态字符串SDS的实现示例

作者:秋天code

Redis没有直接复用C语言的字符串,而是新建了SDS,本文主要介绍了Redis简单动态字符串SDS的实现示例,具有一定的参考价值,感兴趣的可以了解一下

定义

众所周知,Redis是由C语言写的。对于字符串类型的数据存储,Redis并没有直接使用C语言中的字符串。而是自己构建了一个结构体,叫做“简单动态字符串”,简称SDS,比C语言中的字符串更加灵活。

SDS的结构体是这样的:

struct{
	int len; // 数组中已使用的字节的数量,即真实的内容长度 
	int free; // 数组中未使用的字节的数量,即还可以继续存储的内容的长度 
	char buff[]; // 字节数组,用来保存字符串 
};

在C语言中,总是使用长度是N+1的字符数组来保存长度为N的字符串,并且字符数组的最后一个是’\0’结束符,在SDS中,一次申请的字符串的长度比真实的长,所以才会有free这个属性

SDS与C语言字符串相比,优点是:

获取字符串长度

对于C字符串来说,获取一个字符串的真实长度,需要遍历字符串,这就是O(N)的时间复杂度。而在SDS中,用一个len属性保存字符串的真实长度,每次对字符串的修改,都会维护这个len属性因此对于SDS来说,获取字符串的真实长度的时间复杂度是O(1),这确保了获取字符串长度的操作不会成为Redis字符串的性能瓶颈

内存溢出问题

在C字符串中,如果要对字符串进行修改操作,如果忘记了给字符串重新分配足够的空间,就会导致内存溢出问题。在C语言中,并没有内存溢出相关的检查机制,因此可能会导致不可预测的问题产生。

通过SDS的API来操作字符串时,会先检查SDS的空间是否满足修改的要求,如果不满足的话,会自动将SDS的空间扩展至要求的大小,然后执行字符串操作,所以使用SDS来操作字符串,不用担心内存溢出问题。

减少内存分配的次数

对于C语言字符串,因为总是有一个长度为N+1的字符数组来保存一个长度为N的字符串。所以,如果对C字符串进行操作:

而对于SDS来说,不存在这些问题,通过两个机制来解决以上问题

1. 空间预分配

空间预分配机制,用来优化SDS字符串的增长操作。我们认为初始化赋值和拼接操作都是对于SDS的扩容操作。当SDS来扩容一个字符串时,系统不仅会为SDS分配所需的内存空间大小,还会分配额外的未使用空间,即系统分配给SDS的空间大小比真实的字符串长度要大。至于,额外的空间有多大,有以下规则:

通过空间预分配策略,可以减少字符串增长操作的内存分配次数。当进行字符串增长操作时,会先检查free的空间大小是否够增加的长度,如果够,那么直接在真实的数组上操作,无需再进行内存分配操作,并维护free和len的值。如果不够,那么就会进行扩容操作,扩容机制上面说过了。

2. 惰性空间释放

惰性空间释放用来优化字符串的缩短操作。当SDS缩短一个字符串时,还是直接在原始的数组上操作,并维护len和free的值。缩短完成后,程序并不会立即回收释放后的内存,而是使用free属性记录下来,方便下次的字符串长度增加时使用。

二进制安全

C字符串中的字符必须符号某种编码,例如,当编码格式是ASCII时,除了末尾的空字符’\0’外,字符串内容中不可以出现空字符,否则程序在读取字符串时,会误以为这是字符串的结尾。这样的限制使得,C字符串只能保存文本数据,而不能保存图片、音频等二进制数据。而SDS会以二进制的方式来处理存放到buff数组中的数据,程序不会对其中的数据进行限制、过滤等额外操作这就是我们称SDS是字节数组的原因——Redis不是用buff数组来保存字符,而是保存一系列的二进制数据

SDS不是使用空字符’\0’来判断字符串的结尾,而是使用len属性来判断字符串是否结尾如"Redis\0String",C字符串的函数会把’\0’当做结束符来处理,而忽略到后面的"String"。而SDS的buf字节数组不是在保存字符,而是一系列二进制数组,SDS API都会以二进制的方式来处理buf数组里的数据,使用len属性的值而不是空字符来判断字符串是否结束。

参考文章

Redis数据结构——简单动态字符串SDS - 随心所于 - 博客园

Redis设计与实现

到此这篇关于Redis简单动态字符串SDS的实现示例的文章就介绍到这了,更多相关Redis 动态字符串SDS内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文