redis中SDS与C语言中字符串的对比

redis并没有直接重用C语言中的字符串,而是自己定义了一个SDS(SDS结构在另一篇文章中)。在存储时,SDS中的buf数组沿用了C语言以空字符串结尾的惯例,方便重用一部分C语言的函数库。

那么为什么redis要自己定义一个SDS结构呢?

  1. 在C语言中,字符串不会记录自己的长度,如果要获取一个字符串的长度,需要遍历整个字符串,并且计数,直到遇见空字符‘\0’为止,此时时间复杂度为O(n)。而SDS中专门使用len属性来保存字符串的长度,获取字符串长度的时间复杂度为O(1)。(这里说明redis中获取String对象长度的命令是:STRLEN key)
  2. 防止缓冲区溢出。在对SDS修改时,会先检查SDS的alloc值,也就是未使用的空间是不是满足修改所需要的大小,如果不满足,那么会自动将SDS的空间扩展到满足需要的大小。这样避免了因为没有足够的空间而造成buf数组溢出。
  3. C语言中字符串的底层实现是一个N+1长的数组(N就是字符串长度,再加上空字符‘\0’),每次增长或者缩短一个字符串时,都需要对数组进行一次内存重分配操作:(1)增长字符串,如果忘记扩展数组,那么会造成内存溢出;(2)缩短字符串,如果忘记释放多余空间,那么会造成内存泄漏。SDS通过alloc属性来记录未使用空间,解决了这两个问题。SDS使用了两种优化策略:
    1. 空间预分配:用于优化字符串增长。当字符串增长时,不仅仅对SDS进行所需空间的扩展,还会对SDS分配额外的未使用空间。至于这个“分配的额外未使用空间(也就是alloc)”是多少,redis给出了两个公式:
      1. 如果对SDS修改之后,len<1MB,那么alloc的大小=len的大小。例如:进行修改之后的SDS,len=13bit,那么此时alloc也被分配13bit,加上额外1bit用来存放空字符串‘\0’,这时buf数组的实际长度=13+13+1=27bit
      2. 如果对SDS修改之后,len>=1MB,那么alloc=1MB。例如,修改之后SDS的len=30MB,那么此时buf的实际长度=30MB+1MB+1bit
      3. 这样的好处是:在扩展SDS之前总会去检查SDS是否有足够的alloc,假设此时因为前一次扩展分配的alloc能够满足此次的空间需求,那么此时就不必再进行一次扩展,也就是不需要再执行一次内存重新分配。
    2. 惰性空间释放:用于优化字符串缩短。当SDS保存的字符串需要缩短时,不会立即通过内存重分配来回收多余字节,而是将多余字节记录到alloc属性中,等待将来扩展时使用。redis也提供了相应的释放内存空间的API,不用担心内存泄露。
  4. C语言中是根据空字符‘\0’来判断字符串是否已结尾,这样就不能保存例如图片、视频这样的二进制数据。比如现在需要多个空字符来分割多个字符串,如下。那么在C语言中只会读取到“redis”,而读取不到“cluster”;SDS则是根据len属性来判断字符串是否结尾,所以不必担心,这样redis不仅可以保存文本数据,还可以保存任意二进制数据。
‘r’'e''d''i''s''\0''c''l''u''s''t''e''r''\0'

 

以下是来自《redis设计与实现》一书中对C语言字符串和SDS的对比表

C字符串SDS
获取字符串长度的复杂度为O(N)获取字符串长度的复杂度为O(1)
API是不安全的,可能会造成缓冲区溢出API是安全的,不会造成缓冲区溢出
修改字符串长度N次必然需要执行N次内存重分配修改字符串长度N次最多需要执行N次内存重分配
只能保存文本数据可以保存文本数据或者二进制数据
可以使用所有的<string.h>库中的函数可以使用一部分<string.h>库中的函数
  • SDS相关的API可以在sds.h 和 sds.c中查看
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值