Redis之简单动态字符串SDS

Redis是一种非关系型数据库,以key-value的形式进行存储。而其键值都是以字符串的形式进行存储的,却并没有沿用c语言中的字符串,而是自己构建了一种数据类型SDS(Simple Dynamic String)。

我们来看看SDS的结构定义:

struct sdshdr{
	//字节数组,用于保存字符串
	char[] buf;
	//记录了buf数组中已使用的字节数量
	int len;
	//记录了buf数组中未使用的字节数量
	int free;
}

假设我们现在要存储"redis",假设我们buf数组长度为5,那么我们的buf数组中就会保存进redis这个字符串,每个字符占一个字节,因此len应该为5,free为0。但是实际的存储情况是:|r|e|d|i|s|’\0’|,末尾会自动加上一个空字符,是由SDS函数自动完成的,所有对于SDS的使用者来说是透明的,也不会影响到len个free属性。

我们会发现,SDS的格式怎么和C的字符串格式这么像?确实,在一些不会被修改的字符串时还是会使用C的字符串。那创建SDS的目的在哪?下面就来一一说明。

快速获取字符串长度
我们知道,获取C语言字符串的长度需要对底层数组进行遍历,因此需要O(n)的时间复杂度。而SDS的字符串长度我们可以直接通过len属性拿到,为O(1)复杂度。而设置len属性是SDS的api在执行时自动完成的,无需人为干涉。从本质上来看,可以说是用空间换时间的思想了。

杜绝缓冲区溢出
我们知道,C语言字符串在内存中会分配固定的空间,若想扩增一个字符串,但是在这之前却忘记扩大内存,就会出现缓冲区溢出的情况。SDS则避免了这种危害的发生: SDS的API在需要对SDS进行修改时,会事先检查free空间是否足够,若不足够则会扩展空间到需要的大小,然后才执行实际的操作。

空间预分配
C语言在每一次修改字符串时,都需事先对内存大小进行处理。频繁的添加字符串就会频繁的导致内存分配的发生,而Redis是一种数据库,自然有效率上的要求。因此,每次的内存空间不足以支持当前操作时,会进行内存扩增:若SDS修改后的大小<1MB,那么就把len赋值给free,即再预留出一半的空间应对下次的添加操作;若SDS修改后的大小>1MB,就将free设置为1MB。通过空间预分配操作,可以减少内存分配的次数,提高程序的效率。

惰性空间释放
C语言中每次对字符串进行删减时,也需要释放掉不再使用的内存,否则会出现内存泄漏的情况。而SDS移除掉不需要的内容后并没有释放其内存,用free记录空闲未被使用的内存,如此在下次扩充的时候也再需要申请内存。实现内存的复用,在需要的时候再释放内存,因此也不用担心内存泄漏的问题。

二进制安全
C语言是以’\0’结尾,因此只能存储文本文件无法存储二进制文件。为了确保Redis可以适用于各种不同的场景,SDS的API都是二进制安全的,所有的SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,这也是我们将SDS的buf属性称为字节数组的原因。

调用C语言的部分函数
SDS的API会自动在buf数组末尾加上’\0’使其语法格式与C语言相同,因此也可以调用C的某些API达到复用的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值