Redis原理篇--动态字符串SDS

动态字符串SDS

redis中的所有key是字符串,所有value本质上也是字符串,比如 集合set中的每一个 成员 都是一个独立的字符串对象,列表中的每一个 元素 都是一个独立的字符串对象,整个HASH是一个对象,它内部的每一个 字段(field) 和一个字段值(value) 都是一个独立的字符串对象

redis是通过c语言来实现的,但是没有直接使用c语言中的字符串,有几下几点原因

  • 获取字符串长度需要通过运算:C字符串以\0(空字符)结尾,要获取长度必须遍历整个数组直到遇到\0,时间复杂度为O(n),这在高性能数据库如Redis中效率低下。
  • 非二进制安全:C字符串不能存储任意二进制数据,因为它依赖于\0作为结束符。如果数据中包含\0(如一些二进制文件),会被错误截断,破坏数据完整性。
  • 不可修改:C语言字符串常量(如char* s = "hello")是只读的,无法直接扩展或修改其长度,这在动态数据存储中不灵活。

Redis的解决方案:Redis因此构建了自己的字符串结构——SDS(简单动态字符串),它通过设计一个智能结构来支持查找、二进制安全性和动态修改。

SDS底层数据结构

uint8_t (8位无符号整数),可表示的最大值是 255 (因为 2^8 - 1 = 255),因此 len 最多记录 255 字节 的长度,否则会溢出,如果一个 SDS 字符串的实际长度超过 255 字节,Redis 会自动选择更大容量的结构体(如 sdshdr16/sdshdr32)。

falgs :类型自动匹配:SDS 根据字符串长度动态选择头类型
#define SDS_TYPE_5  0  // 超短字符串(≤31B)
#define SDS_TYPE_8  1  // 短字符串(≤255B)
#define SDS_TYPE_16 2  // ≤65535B
#define SDS_TYPE_32 3  // ≤4GB
#define SDS_TYPE_64 4  // 超大字符串
 

为了和c语言兼容会在结尾带上\0,但是我们的sds再去读取这个字符串的时候不会以结束标识作为标准来读,因为已经有长度了,会从开始读len个长度停止

SDS特性

  1. 初始状态:存储值 "hi" 时,SDS 头部记录 len=2, alloc=2(分配空间=实际占用)

    | len: alloc:2 | flags:1 | h | i | \0 |
    
  2. 追加数据 ",Amy"

    • 新长度 = 2+4=6(需扩容)
    • 内存预分配规则触发
      • 若新增长度 <1MB → 分配 2倍新长度+1
      • 若新增长度 ≥1MB → 分配 新长度+1MB+1
    • 本例中 6 < 1MB → 新空间为 6*2+1=13字节 → 记录 alloc=12(不含结尾\0

为什么需要内存预分配?

  1. 优化高频修改性能

    • 若每次追加字符串都精确分配空间(如 len=6 → alloc=6),后续继续追加时需重复申请内存
    • 预分配额外空间 让连续多次追加(如循环中逐步构建字符串)不再触发扩容操作
  2. 减少内核态和用户态的转换(申请内存空间的时候是需要切换到内核态的)
    • 场景对比:
      • 未预分配:追加 "A"+"B"+"C" → 发生3次 realloc() → 3次系统调用(最差情况)
      • 预分配后:追加 "A"+"B"+"C" → 仅首次需扩容时触发了1次 realloc()
        → 其余操作直接写入预留空间→ 减少66%的系统调用数

alloc申请的长度不包括最后的结束标识符,所以这里是12

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值