Redis-简单动态字符串(Simple Dynamic String )

Redis的设计与实现

<一> 简单动态字符串(Simple Dynamic String )

当Redis需要的不仅仅是一个字符串字面量,而是一个可以被修改的字符串值时,Redis就会使用SDS表示字符串值.

一. SDS的定义

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

SDS遵循C字符串以空字符结尾的惯例,保留空字符的1字节空间不计算在SDS的len属性里面,并为空字符分配额外的1字节空间,

这样做的好处是SDS可以重用一部分C字符串函数库的函数.

二. 常数复杂度获取字符串长度

首先C字符串并不记录字符串的长度,所以只能使用Strlen()函数,遍历整个字符串并计数,直到遇到空字符串为止.时间复杂度为O(n).

而对于SDS来说,它的结构体里面保存了属性 len ,记录了当前SDS保存的字符串的长度,即SDS的长度为 Len+1(空字符),Resis将获取字符串长度的复杂度降低到了O(1).

三.杜绝缓冲区溢出

//有两个相邻的字符串S1 = "hello"; S2 = "teacher"
strcat(s1,"Redis");// 此函数将Redis追加到s1的后面
//如果在执行以上函数是,没有给s1分配足够的空间,s1的数据将会溢出到s2所在的空间中.
//s2 = "Rediser";

与C字符串不同的是,SDS的空间分配策略完全杜绝了缓冲区溢出的可能性:

当SDS的API对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的要求,如果不满足,API自动将SDS的空间扩展至执行修改所需的大小,然后再执行修改操作.

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

对于C字符串来说每次增长或者截断一个C字符串是,程序总要对保存这个字符串数组进行一次内存重分配操作:

  • 如果是执行的是增长字符串的操作,在执行这个操作之前,程序通过内存重分配来扩展底层数组的空间大小,如果忘了这个操作->缓冲区溢出.
  • 如果执行的是缩短字符串的操作时,例如截断(trim)操作时,在执行这个操作之后,`程序需要通过内存重分配来释放内存不再使用的那部分空间,如果忘了->会产生内存泄漏.

对于这种内存重分配策略,在一般程序上每次修改执行一次内存重分配是可以接受的,但对于Redis作为数据库,数据频繁被修改,执行内存重分配的时间会占用修改的大部分时间,会对性能造成影响.

SDS实现了空间预分配和惰性空间释放的两种优化策略.

空间预分配

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

  • 当SDS的长度(len)小于1MB时,这时会分配和len大小相同未使用空间(free)
  • 当SDS的长度大于1MB时,这是会分配1MB的未使用空间

Redis会减少连续执行字符串增长操作所需的内存重分配次数.

惰性空间释放

用于优化SDS的字符串缩短操作,当SDS需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是用free记录这些字节的数量,以供将来使用.

五. 二进制安全

C字符串除了末尾的空字符之外,里面是不能包含空字符的,这就限制了C字符串只能保存文本数据,不能保存图像视频压缩文件的二进制数据.

SDS的API都是二进制安全的(binary-safe),所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,程序不会其中的数据进行任何的修改,过滤.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值