《深入浅出redis》

1 数据结构与对象

1.1 SDS(simple dynamic string)定义

  /*
   * 保存字符串对象的结构
   */
  struct sdshdr {

      // buf 中已占用空间的长度
      int len;

      // buf 中剩余可用空间的长度
      int free;

      // 数据空间
      char buf[];
  };
  • free属性的值为0,表示这个SDS没有分配任何未使用空间
  • len属性的值为5,表示这个SDS保存了一个5字节长的字符串
  • buf属性是一个char类型的数组,最后以’\0’结尾。空字符不计算在SDS的len中

1.2 SDS与C字符串的区别

C字符串不能满足Redis对字符串在安全性、效率以及功能方面的要求

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

C字符串不记录自身的长度信息,因此获取一个C字符串的长度,需要遍历整个字符串,时间复杂度为 O(n), 而SDS里有len字段保存了字符串本身的长度,时间复杂度为O(1),确保了获取字符串长度不会成为redis的性能瓶颈

1.2.2 杜绝缓冲区溢出

C字符串不记录自身自身长度,因此容易造成缓冲区溢出,如:

# 如果用户为dest分配的空间不够,就会产生溢出,可能会导致src的内容被覆盖
char *strcat(char *dest, const char *src);

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

1.2.3 减小内存重分配次数

SDS为了避免每次修改都要进行一次内存分配,使用free字段(未使用空间)来解除字符串长度和底层数组长度之间的关联,实现了空间预分配和惰性空间释放两种优化策略。

  1. 空间预分配
  • 如果对SDS进行修改后,SDS的长度(len的值)小于 1MB,那么程序将分配2倍的len,即len和free的值相等,buf的实际长度为2len + 1
  • 否则(len>=1MB),程序将分配len+1MB,buf的实际长度为len+1MB+1
  1. 惰性空间删除
    当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重新分配来回多出来的空间,而是使用free属性将这些字节的数量记录起来,并等待将来使用

1.2.4 二进制安全

C字符串里不能包含空字符串(’\0’),而SDS使用len来判断字符串是否结束,因此SDS的buf可以存放二进制数据

1.2.5 兼容部分C字符串函数

SDS的API虽然都是二进制安全的,但是一样遵循C字符串以空字符串结尾的惯例。这些API总是为SDS保存的数据的末尾设置空字符,并且总会在为buf数组分配空间时多分配一个字节来容纳这个空字符。
好处:

  • redis不必自己专门写函数来对比SDS与C字符串了,可以直接使用string.h的函数库,如:
strcat(c_string, sds->buf);

猜你喜欢

转载自blog.csdn.net/kaydxh/article/details/106290977