Redis SDS 深入一点,看到更多!
1、什么是SDS?
Redis 自定的字符串存储结构,关于redis,你需要了解的几点!中我们对此有过简要说明。
Redis 底层是用C语言编写的,可是在字符存储上,并未使用C原生的String类型,而是定义了自己的字符串结构 Simple Dynamic Stirng,简称SDS。
SDS基本结构如下:
struct sdshdr {
int len; // 记录buf数组中已使用字节的数量,等于SDS所保存字符串的长度
int free; // 记录buf数组中未使用字节的数量
char buf[];// 字节数组,用于保存字符串
};
例如,字符串“Redis”存储示意图为:
图1
当前存储字符串长度为5,未使用长度为0,字节数组存储的字符为“Redis ”。
这里需要注意的是:内部数据数组存储字符串形式符合C语言要求,以‘ ’结尾。且len字符串长度不包含结尾标识符‘ ’。
buf[]的这种遵循C语言形式的存储,使得Redis可以直接使用C语言的相关字符串函数进行SDS对象的操作。
二、SDS的优势
1、O(1)时间复杂度获取字符串长度
SDS内部维护着一个字符串长度的len变量,可以直接读取,时间复杂度为O(1)。
对于传统的C字符串:字符+“ ”,想要获取字符长度,则需要遍历整个字符串,直到遇到结束字符,时间复杂度为O(n)。
2、缓冲区溢出规避
所谓缓冲区溢出即所需要的内存超出了实际的内存。因此对于C字符串来说,要特别注意内存分配,回收使用问题。
比如,向一个现有字符串内添加特定字符时,需要保证当前已经分配了这足够的内存。
图2
与C不同的是,SDS的空间预分配策略可以避免缓冲区溢出发生,
当需要对SDS进行操作时,首先会检查当前空间是否满足需求,不足则扩展当前分配空间。内存检查相对于C变成了内部预置操作。
3、减少内存重分配次数
上面我们讲到过,C字符操作前都需要进行内存的分配操作,同时,操作完成后,也需要进行相应的内存回收操作。一次操作至少涉及一次内存分配操作。
大家都知道内存的重分配是一个比较复杂且需精细控制的过程,耗时耗资源。针对此弊端,Redis 在SDS内存配置策略上采用了空间预分配+惰性删除相结合的策略。
a)空间预分配:
空间预分配用于优化SDS字符扩展操作。
所谓预分配,也即是说在一次扩展操作中,扩展的空间大小会大于实际需要的空间大小。 如下,图1执行图2操作后SDS变更为:
图3
预分配空间的大小基于以下规则计算:
SDS len<1M:分配len长度空间作为预分配空间;
SDS len>=1M:分配1M空间作为预分配空间;
这样,在下次进行字符操作的时候,如果所需要的空间小于当前SDS free空间,则可以直接行操作,而不需要再执行内存扩展,重分配操作。
SDS的预分配机制使得一次扩展操作所需的内存重分配次数变为<=1。
b)惰性删除机制
所谓惰性删除,即调整删除SDS中部分数据时,不会立刻执行内存重分配,而是会保留空出来内存,并更新内部free属性。以备将来有字符扩展需求,可以直接使用。
当然,Redis也提供了主动释放未使用内存的方法。
如下,删除“ent”之后的SDS结构:
图4
SDS的内存分配机制,尤其对于以写为主的应用场景,能够提供更加优异的性能表现。
3、二进制安全
C字符串由于特殊的编码要求只能保存文本数据。
SDS相关的功能方法会以二进制的形式来操作SDS存储的数据,没有任何中间操作,存储最原始的数据,因此不会有字符层面的因素影响。
SDS可以保存任何源的二进制数据,字符、图片、文件或者序列化的对象等等。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【设计模式】692- TypeScript 设计模式之发布-订阅模式
- 强网杯-upload
- 基于暗通道去雾算法
- 全套 | 人脸检测 & 人脸关键点检测 & 人脸卡通化
- 使用Jenkins Dashboard插件可视化部署
- 全面综述:图像特征提取与匹配技术
- opencv+python制作硬核七夕礼物
- opencv+python制作硬核七夕礼物
- 七夕节也要学起来,哈希哈希哈希!
- 目标检测器性能评估工具包
- istio 1.7发布
- AkShare-中国宏观-工业品出厂价格指数
- AkShare-中国宏观-采购经理人指数
- Python 为什么没有 void 关键字?
- 【特征提取+分类模型】4种常见的NLP实践思路