redis-原理-数据结构-SDS(一)

什么是SDS

1.redis没有直接使用C的字符串,而是自己实现了字符串的实现名叫SDS

2.c的字符串只会用在值不会改变的地方,比如redislog打印

SDS的应用场景

1.

本地:0>set msg hellowrd
OK

键和值都是字符串对象,底层通过SDS实现

2.

本地:0>rpush fruits apple banana cherry
3

键是字符串对象 由SDS实现, 值是列表对象,列表对象包含三个字符串对象 分别由SDS实现

SDS结构定义

SDS遵循C的字符串定义,以空字符串()作为字符串的结尾,空字符串1字节不计算在len里面,好处就是直接使用C字符串的函数如:print(%,s-buf)

带有未使用字节数量的SDS

SDS和C字符串的区别

1. C字符结构是没有记录长度的,如果要获取长度必须遍历字符,复杂度是0(N)

2.SDS是记录了字符串长度获取字符串长度是0(1)如:

本地:0>strlen msg
8

缓冲区溢出

因为C不记录字符长度,如果在进行扩容时,只能假定内存足够,如果不足够则会造成缓冲区溢出

扩容前 S1 和S2 2个字符紧邻,我们要对S1进行扩容,因为没有记录长度我们并不知道内存是否足够,执行:strcat(s1,"cluster")

扩容前:

扩容后

SDS如何解决缓冲区溢出 

与C不同因为SDS记录了未使用空间,在扩容前会先判断空间是否足够,如果不够sdscat(redis SDS的拼接API)先扩容空间

如执行

strcat(s1,"cluster")

扩容前

扩容后

二进制安全

C的字符必须符合某种编码(ASCII),并且除了字符串末尾之外字符串里面不能包含空字符,否则会误认为字符串结尾,这些使得C只能保存文本而不能保存图片、视频、音频、压缩文件这样的二进制数据

因为SDS并没有使用空字符()判断字符的结尾而是len来判断,使用SDS可以保存图片 视频 音频等二进制数据

SDS对于扩容的优化

空间预分配

当我们对SDS空间进行扩展的时候,SDS除了分配必要的空间,还会为SDS分配额外的未使用空间 free记录。

公式:

      1.如果对SDS进行修改后,len长度小于1M则分配len同样大小的未使用空间 len+free+1为当前SDS占用空间 1为结尾空字符占用空间

      2.如果对SDS进行修改有len长度大于1M则会额外分配1M未使用空间   len+1m+1为当前SDS占用空间 1为结尾空字符占用空间

当每次扩展SDS API都会检查free是否有足够空间,如果有则不执行扩展 直接使用 减少了分配次数,

通过空间预分配优化,将连续增长N次字符串需要扩展N次,变为最多N次

惰性分配

当SDS缩短字符长度时候,并不会完全释放,只是将多出来的空间记录到free

如果移除字符串中所有的 x和y

移除前

移除后

 将来需要拼接字符时则可以减少扩容

预分配和惰性分配空间浪费解决方式

通过上面的意思,会让我们觉得会浪费空间,其实SDS也提供了API让我们可以真正的释放SDS未使用空间,所以不用担心预分配和惰性分配造成的浪费

SDS的优点

1.比起C字符串,SDS获取字符串长度是O(1)

2.比起C字符串,不会缓冲区溢出

3.减少字符串扩容时的分配次数

4.二进制安全

5.兼容C字符串,可以重用C字符串的函数库

原文地址:https://www.cnblogs.com/LQBlog/p/14477370.html