Redis源码篇- SDS

Redis对于String类型,底层使用的是SDS(简单动态字符串),而不是常规的C语言的字符数组 。

通常在C中,定义一个字符串,方式是通过字符数组表示,同时结尾使用/0表示结束:char [] = "hello,world/0";
计算字符串长度则为:遍历char[],复杂度O(n) ;  

常规的C中字符串使用带来的问题:

  • 获取长度复杂度为O(n),Redis在万+的处理时,会收到一定性能影响
  • 常规字符串读取,遇到"/0"会自动结束,对于Redis来说是二进制不安全的,因为可能存在String类型values就是存在\0的内存,此时就会被错误截断,这也是二进制不安全
  • 同时当需要对字符串进行修改时,需要重新分配空间,否则会有缓存区溢出或者内存泄漏的风险。

SDS是基于C字符串之上的一种封装,用来解决C字符串的缺陷问题,这种封装的特殊的数据结构就是SDS。

数据结构:sdshdr5

sdshdr8

Redis源码篇- SDS_第1张图片

 SDS数据结构体名称sdshdr,分为5、8、16、32、64 5种,根据数据内容大小自动选择适合SDS结构存储,节省空间。

sdshdr 和 sds是什么关系?
  SDS是一个字符数组指针,指向了sdshdr种buf[] , 所以可以理解为SDS就是字符数组,但是可以通过SDS获取到
sdshdr中len等相关信息(通过指针位数的计算,获取)

Redis源码篇- SDS_第2张图片

 Redis为什么使用SDS,高效?

1、高效获取字符串长度,复杂度O(1)

常规的C字符串长度获取,需要遍历Char数组,复杂度O(n),SDS中可以通过,指针的移动获取到len等内容,复杂度O(1) ; 比如多次使用strlen指令会提高效率,记住Redis是将面临万+的请求处理的,所以该提效是非常有用的。

2、减少内存重新分配次数,即减少内核调用

常规C字符串中,对内容进行修改,需要重新分配内存大小 ,比如ABC存储时,分配大小为3字节:

  •      如果新增内容时,需要重新进行内存分配,否则会缓冲区溢出。
  •       如果减少内容时,也需要重新进行内存分配,否则会有内存溢出风险

SDS结构体系中,存有当前总申请空间大小alloc 和使用空间大小use , 于是就能得出free空间大小(在版本redis就是存有free属性);SDS就是通过free实现了
空间预分配惰性空间释放两种优化。 

· 空间预分配

用于添加字符串内容时
 : 此机制有点像java中list内存分配了 。
大致策略 : 当SDS需要更多内存分配时,不久分配需要的空间,也会分配更多的free空间,这样下一次append内容时,就不会在重新分配空间,当再一次需要分配空间时,依然会多分配空间。(当newlen< 1mb,则free分配 = len大小 , 当newlen >=1mb,则分配free = 1bm)

· 惰性空间释放
用于缩短字符串内容时

大致策略 : 当SDS需要缩短字符串内容时,不会立马释放多余空间,而是将多余的空间大小记录到free中,以供下次使用。(当达到某种条件时-free > 10% * len
,通过客户端操作触发,再去释放多余空间 -- 惰性

 3、二进制安全

c字符串中的字符必须符合某种编码(ASCII),不能保存图片,音频等这样的二进制数据在读取时必须按照格式进行读取。
SDS是通过len来决定截取字符数组的内容的,而不是像常规 C字符串通过/0 ,来截取,所以SDS保留了完整的字符串内容,所以是二进制安全的。

4、节省空间

Redis使用SDS会根据内容大小,使用不同结构的SDS,比如sdshrd5、8、16等,不同结构体空间大小不同,对于小的字符数组内容就是用小SDS结构,节省空间。

地址:

你可能感兴趣的:(redis,java,数据库)