Redis的数据结构介绍(一)简单动态字符串SDS

Redis的数据结构介绍(一)简单动态字符串SDS

  • 1.SDS的定义
  • 2.杜绝缓存区溢出
  • 3.空间预分配
  • 4.惰性空间释放
  • 5.二进制安全
  • 6.总结

1.SDS的定义

strcuct sdshdr{
	//记录buf数组中已使用字节的数量
	//等于SDS所保存字符串的长度
	int len;
	//记录buf数组中
	int free;
	char buf[];
}

SDS遵循C字符串以空字符结尾的惯例,保存空字符1字节不计算在SDS的len属性里面,并且为空字符分配额外的一字节空间,以及添加空字符到字符串末尾等操作,都是由SDS函数自动完成的,所以这个空字符对使用者完全透明。这样的好处是可以使用一部分C字符串函数库里面的操作。

2.杜绝缓存区溢出

因为C语言字符串不记录自身长度,所以strcat 假定用户在执行这个函数时,已经为dest分配了足够多的内存,可以容纳src字符串中的所有内容,而一旦这个假定不成立时,就会产生缓存区溢出。
与C语言不同的是SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性:当SDS API需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改的所需大小,然后执行实际的修改操作,所以使用SDS既不需要手动修改SDS的空间大小,也不会出现前面所说的缓冲区溢出问题。

3.空间预分配

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

  1. 如果对SDS进行修改之后,SDS的长度将小于1MB,那么程序分配和len属性同样大小的未使用空间,这时SDS len 属性的值将和free的值相同。举个例子,如果进行修改之后,SDS的len将变为13字节,那么程序也会分配13字节未使用空间,SDS的buf数组实际长度将变为13 + 13 + 1 =27字节(额外的一字节将用于保存空字符串)。
  2. 如果对SDS进行修改之后,SDS的长度将大于等于1MB,那么程序会分配1MB的未使用空间。举个例子,如果进行修改之后,SDS的len将变为30MB,那么程序会分配1MB的未使用空间,SDS的buf数组实际长度将为30MB + 1MB + 1byte。通过空间预分配策略,Redis可以减少连续执行字符串增长操作所需要的内存重分配次数。

4.惰性空间释放

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

5.二进制安全

SDS不是根据判断/0来判断字符串释放结束。所以用户写入数据是什么样的使用就是什么样的。

6.总结

C字符串 SDS
获取字符串长度的复杂度为O(N) 获取字符串长度的复杂度为O(1)
API是不安全的,可能会造成缓冲区溢出 API是安全的,不会造成缓冲区溢出
修改字符串长度N次必然需要执行N次内存重分配 修改字符串长度N次最多需要执行N次内存重分配
获只能保存文本数据 可以保存文本或者二进制数据
可以使用库中的函数 可以使用一部分的函数

本文参考黄健宏的《Redis设计与实现》,点击关注本人的微博:不会敲代码的小白。会陆续跟新微博技术文章。

你可能感兴趣的:(Redis)