[redis] 数据结构 -- 字符串

为什么使用 SDS(Simple Dynamic String,动态字符串)

  1. 常数复杂度获取字符串长度 O(1)
  2. 杜绝缓冲区溢出
  3. 减少修改字符串时带来的内存重分配次数(最多扩容 n 次,空间预分配、惰性空间释放)
  4. 二进制安全,SDS API 会以处理二进制的方式来处理 SDS 存放在 buf 数组里的数据( buf 里保存的是二进制数据)

知识点

[注] 相关源码保存位置:src/redis.h、src/sds.h、src/sds.c。

  • Redis 只会使用 C 字符串作为字面量

  • 在 redis 中字符串叫 SDS(Simple Dynamic String,动态字符串)

  • SDS 结构如下 – 是一个带长度信息的字节数组。

    struct SDS<T> {
      T capacity; // 数组容量,1byte
      T len; 	  // 数组长度,1byte
      byte flags; // 特殊标识位,1byte
      byte[] content; // 数组内容
    }
    
  • 字符串存储方式分为

    • embstr 长度 44byte 及以下
    • raw 长度 45byte 及以上
  • embstr 最大长度为 44byte 的原因如下

    • 所有的 Redis 对象都有下面的这个结构头,此结构占据 16byte 的存储空间
      struct RedisObject {
          int4 type;        // 4bits
          int4 encoding;    // 4bits
          int24 lru;        // 24bits
          int32 refcount;   // 4bytes
          void *ptr;        // 8bytes,64-bit system
      } robj;
      
    • 此外 SDS 结构体至少要占用3byte的空间,所以分配一个字符串的最小空间占用为 19byte (16+3)
    • 内存分配器 jemalloc/tcmalloc 等分配内存大小的单位都是 2、4、8、16、32、64 等等,为了能容纳一个完整的 embstr 对象,jemalloc 最少会分配 32byte 的空间,如果字符串再稍微长一点,那就是 64byte 的空间。如果总体超出了 64byte,Redis 认为它是一个大字符串,不再使用 emdstr 形式存储,而该用 raw 形式。
    • SDS 结构体中的 content 中的字符串是以字节 \0 结尾的字符串,之所以多出这样一个字节,是为了便于直接使用 glibc 的字符串处理函数,以及为了便于字符串的调试打印输出。

    所以 embstr 最大能容纳的字符串长度就是 44byte (64-16-3-1)。

扩容策略

  • 字符串在长度小于 1M 之前,扩容空间采用加倍策略,也就是保留 100% 的冗余空间。
  • 当长度超过 1M 之后,为了避免加倍后的冗余空间过大而导致浪费,每次扩容只会多分配 1M 大小的冗余空间。

C 字符串和 SDS 之间的区别

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值