栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Redis读书笔记(1)-数据结构-1.简单动态字符串

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Redis读书笔记(1)-数据结构-1.简单动态字符串

简单动态字符串(simple dynamic string,SDS)为Redis的一种抽象类型,并将SDS用作Redis的默认字符串表示。
对于C字符串的使用【用在一些无须对字符串值进行修改的地方】例如打印日志 : redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
而对于需要修改字符串值时,Redis就使用SDS表示字符串,包含字符串的键值对由SDS实现。因此,键值对也是一个字符串对象;除此之外,SDS还用做缓冲区(buffer)(AOF)

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

struct sdshdr { //所保存字符串的长度 int len; //记录buf数组中未使用字节的数量 int free; //字节数组,用于保存字符串 char buf[];};如图:

* SDS与C字符串的区别

C使用N+1长度的字符数组表示长度为N的字符串,并在尾部填入‘/0’表示结尾

  • 常数复杂度获取字符串长度
    C获取长度,需要遍历一遍(对遇到的每个字符进行计数,直到遇到代表字符串结尾的空字符为止),复杂度O(n),而SDS获取长度复杂度为O(1),因为SDS有len字段标记长度。

  • 杜绝缓冲区溢出
    C:内存紧邻的两个字符串s1,s2,当对s1进行修改strcat时,忘记了给s1分配足够的内存,就会溢出到s2,导致s2的数据被修改。而SDS则会自动判断字符串长度,拓展S的内存空间再执行,SDSCAT操作。

  • 减少修改字符串时带来的内存重分配次数
    由于N长度字符数组的字符串,底层实现为N+1长度的字符数组的这种关联关系,导致每一次对字符串进行增减操作,都导致程序对内存的重新分配。

如果程序执行的是增长字符串的操作,比如拼接操作(append),那么在执行这个操作之前,程序需要先通过内存重分配来扩展底层数组的空间大小——如果忘了这一步就会产生缓冲区溢出。
·如果程序执行的是缩短字符串的操作,比如截断操作(trim),那么在执行这个操作之后,程序需要通过内存重分配来释放字符串不再使用的那部分空间——如果忘了这一步就会产生内存泄漏。

碍于redis与普通的程序不同,redis为处理频繁出现的操作,其对速度的要求很高,频繁地进行内存重新分配,耗时,影响性能。
而SDS通过未使用空间(free)解除了字符串长度和底层数组长度之间的关联,通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略。

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

  • 如果对SDS进行修改之后,SDS的长度(也即是len属性的值)将小于1MB,那么程序分配和len属性同样大小的未使用空间,这时SDS
    len属性的值将和free属性的值相同。举个例子,如果进行修改之后,SDS的len将变成13字节,那么程序也会分配13字节的未使用空间,SDS的buf数组的实际长度将变成13+13+1=27字节(额外的一字节用于保存空字符)。
  • 如果对SDS进行修改之后,SDS的长度将大于等于1MB,那么程序会分配1MB的未使用空间。举个例子,如果进行修改之后,SDS的len将变成30MB,那么程序会分配1MB的未使用空间,SDS的buf数组的实际长度将为30MB+1MB+1byte。

通过空间预分配策略,Redis可以减少连续执行字符串增长操作所需的内存重分配次数。
扩展之前,会判断未分配空间(free)是否足够,足够直接分配,否则执行计算扩展。

SDS惰性空间释放策略并不会在执行字符串缩减的时候释放缩减的空间,而是标记在(free),避免了一次空间重新分配,并且在下次扩展的时候通过判断大小是否直接分配(free)预留的大小。
SDS避免了缩短字符串时所需的内存重分配操作,并为将来可能有的增长操作提供了优化。

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


由于N长度字符数组的字符串,底层实现为N+1长度的字符数组的这种关联关系,导致每一次对字符串进行增减操作,都导致程序对内存的重新分配。
如果程序执行的是增长字符串的操作,比如拼接操作(append),那么在执行这个操作之前,程序需要先通过内存重分配来扩展底层数组的空间大小——如果忘了这一步就会产生缓冲区溢出。
·如果程序执行的是缩短字符串的操作,比如截断操作(trim),那么在执行这个操作之后,程序需要通过内存重分配来释放字符串不再使用的那部分空间——如果忘了这一步就会产生内存泄漏。
碍于redis与普通的程序不同,redis为处理频繁出现的操作,其对速度的要求很高,频繁地进行内存重新分配,耗时,影响性能。
而SDS通过未使用空间(free)解除了字符串长度和底层数组长度之间的关联,通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略。
空间预分配用于优化SDS的字符串增长操作:当SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS分配修改所必须要的空间,还会为SDS分配额外的未使用空间。
其中,额外分配的未使用空间数量由以下公式决定:·如果对SDS进行修改之后,SDS的长度(也即是len属性的值)将小于1MB,那么程序分配和len属性同样大小的未使用空间,这时SDS > len属性的值将和free属性的值相同。
举个例子,如果进行修改之后,SDS的len将变成13字节,那么程序也会分配13字节的未使用空间,SDS的buf数组的实际长度将变成13+13+1=27字节(额外的一字节用于保存空字符)。
·如果对SDS进行修改之后,SDS的长度将大于等于1MB,那么程序会分配1MB的未使用空间。
举个例子,如果进行修改之后,SDS的len将变成30MB,那么程序会分配1MB的未使用空间,SDS的buf数组的实际长度将为30MB+1MB+1byte。通过空间预分配策略,Redis可以减少连续执行字符串增长操作所需的内存重分配次数。
扩展之前,会判断未分配空间(free)是否足够,足够直接分配,否则执行计算扩展。惰性空间释放用于优化SDS的字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用free属性将这些字节的数量记录起来,并等待将来使用。

SDS惰性空间释放策略并不会在执行字符串缩减的时候释放缩减的空间,而是标记在(free),避免了一次空间重新分配,
并且在下次扩展的时候通过判断大小是否直接分配(free)预留的大小。
SDS避免了缩短字符串时所需的内存重分配操作,并为将来可能有的增长操作提供了优化。
总结:

SDS 相关的API

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/677901.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号