当前位置: 首页 > news >正文

Redis中的sdshdr的len和alloc那块的知识点详解

文章目录

      • 核心比喻:一个可以伸缩的水瓶
      • 场景一:创建一个新字符串
      • 场景二:追加字符串(触发“空间预分配”)
      • 场景三:再次追加字符串(利用空闲空间)
      • 场景四:缩短字符串(惰性空间释放)
      • 总结

我们用一个非常形象的比喻和几个实际的例子,来彻底搞懂 lenalloc 这两个概念。

核心比喻:一个可以伸缩的水瓶

想象一下,Redis 的一个字符串(SDS)就是一个“智能水瓶”,而 sdshdr 就是瓶身上的刻度标签。

  • len (length): 代表瓶子里当前装了多少水。这是字符串的实际、有效长度。
  • alloc (allocation): 代表这个瓶子的最大容量。这是系统为这个字符串总共分配了多大的内存空间(不含头部和末尾的 \0)。
  • 空闲空间 (free space): alloc - len,就代表瓶子里还能装多少水,也就是预留的空闲内存。

关键点:瓶子的容量 (alloc) 永远大于或等于瓶子里的水量 (len)。

这个设计的核心目的,就是避免频繁地“换瓶子”。因为“换瓶子”(内存重分配)是一个非常耗时的操作。如果每次只加一滴水,你都要去找一个大小刚刚好的新瓶子,效率会非常低下。


场景一:创建一个新字符串

当你执行 SET mykey "hello" 时,Redis 创建了一个 SDS 字符串。

  • 字符串 “hello” 的长度是 5。
  • Redis 内部会创建一个 sdshdr 和一块内存来存放它。假设它选择 sdshdr8 类型(头部占3字节)。
  • 为了效率,它可能不会只分配刚刚好的5字节,但我们为了简化,先假设它就是这么做的。

内存状态:

  • len = 5 (瓶里有5个单位的水)
  • alloc = 5 (瓶子总容量是5)
  • 内存布局大致如下:
    [sdshdr8: len=5, alloc=5] ['h','e','l','l','o','\0']
    
    此时,空闲空间 alloc - len 为 0。瓶子是满的。

场景二:追加字符串(触发“空间预分配”)

现在,我们来追加内容,执行 APPEND mykey " world"

  1. 检查空间:

    • 要追加的字符串 " world" 长度为 6。
    • API 首先检查空闲空间:alloc(5) - len(5) = 0
    • 0 < 6,空间不够!需要“换个大瓶子”(重新分配内存)。
  2. 执行空间预分配(The Magic):

    • 新的字符串总长度将是 len(5) + 6 = 11
    • Redis 不会只分配 11 字节的新空间,它会想:“你既然开始追加了,很可能等下还会再追加。”
    • 于是它启动空间预分配策略:新分配的容量 alloc 会是 新长度的两倍 (在字符串总长小于1MB时)。
    • 新的 alloc = 11 * 2 = 22
    • Redis 会申请一块能容纳 22 个字符的新内存空间,并把旧内容 “hello” 和新内容 " world" 拷贝进去。

内存状态更新:

  • len = 11 (现在瓶里有11个单位的水)
  • alloc = 22 (但瓶子的总容量是22!)
  • 内存布局大致如下:
    [sdshdr16: len=11, alloc=22] ['h','e','l','l','o',' ','w','o','r','l','d','\0', ...11 bytes free... ]<---- len = 11 ----> <---- free = 11 ----><----------------- alloc = 22 ----------------->
    
    现在,alloc(22) - len(11) = 11,我们有 11 个字节的空闲空间

场景三:再次追加字符串(利用空闲空间)

我们继续追加,执行 APPEND mykey "!!!"

  1. 检查空间:

    • 要追加的 “!!!” 长度为 3。
    • API 检查空闲空间:alloc(22) - len(11) = 11
    • 11 >= 3,空间足够!不需要重新分配内存!
  2. 原地修改:

    • 直接在 buf 的末尾把 “!!!” 写进去。
    • 只更新头部的 len 字段。

内存状态更新:

  • len = 11 + 3 = 14 (水变多了)
  • alloc = 22 (瓶子还是那个瓶子,容量没变)
  • 内存布局大致如下:
    [sdshdr16: len=14, alloc=22] ['h','e','l','l','o',' ','w','o','r','l','d','!','!','!','\0', ...8 bytes free... ]<------ len = 14 ------> <---- free = 8 ----><------------------- alloc = 22 ------------------->
    
    这个操作非常快,因为它避免了最耗时的内存分配和数据拷贝。

场景四:缩短字符串(惰性空间释放)

现在,我们把这个 key 的值改成一个很短的字符串,执行 SET mykey "Hi"

  • Redis 会直接用 “Hi” 覆盖掉 buf 开头的内容。
  • 然后,它只会更新 len 字段。

内存状态更新:

  • len = 2 (水变得很少)
  • alloc = 22 (但瓶子还是那个大瓶子!)
  • 内存布局大致如下:
    [sdshdr16: len=2, alloc=22] ['H','i','\0','l','o',' ','w','o','r','l','d', ...garbage data..., ...free space... ]<len=2> <----------------- free = 20 -----------------><------------------- alloc = 22 ------------------->
    
  • 这被称为惰性空间释放。Redis 不会立即把多余的 20 字节空间还给操作系统。它会保留这些空间,因为你可能马上又会执行 APPEND 操作,这样就又可以利用上这些预留空间了。

总结

lenalloc 的设计哲学,是典型的用空间换时间的思想。

  • len 提供了 O(1)O(1)O(1) 时间复杂度的长度获取能力,并且是二进制安全的基础。
  • alloc 配合空间预分配惰性释放策略,极大地减少了内存重分配的次数,这是 Redis 字符串追加(APPEND)操作如此高效的关键所在。它将多次可能发生的、耗时的内存操作,均摊到了一次操作中。
http://www.dtcms.com/a/316723.html

相关文章:

  • 【经验记录贴】在windows系统中启动服务
  • CMU-15445(7)——PROJECT#2-BPlusTree-Task#2Task#3
  • BGA 芯片贴片加工关键注意事项
  • Fiddler 中文版实战指南,如何构建高效的 API 调试工作流?
  • 第13届蓝桥杯Scratch_选拔赛_真题2021年11月27日
  • 老旧体育场馆照明翻新:预算有限?3 步实现 “低成本升级”
  • 在具身智能火热加持下,看 2025 年机器人学术年会中的热点主题。PNP机器人展示力控、灵巧手捕捉等案例。
  • 利用链上数据进行数字资产量化因子发现
  • 计划任务:被忽视的网络与系统安全边界
  • Linux-Day10.系统安全保护web服务管理
  • 【springcloud的配置文件不生效】
  • Linux系统:基础I/O
  • 【RK3576】【Android14】Uboot下fastboot命令支持
  • 闸机控制系统从设计到实现全解析:第 4 篇:Redis 缓存与分布式锁实现
  • JavaScript 概述
  • Linux 逻辑卷管理:LVM 原理与 Stratis、VDO 特性对比
  • Vue2博客项目笔记(第一天)
  • 防御保护3-4
  • STM32CubeIDE新建项目过程记录备忘(八)使用通用定时器中断生成PWM波形
  • LINUX 85 SHElL if else 前瞻 实例
  • MLS学习
  • vue3 计算属性
  • Docker 容器内进行 frp 内网穿透
  • 关于怎么知道linux(ubuntu)系统交叉编译器的命令的方法:
  • web-vue工作流程
  • 从AUTOSAR角度理解CAN以及CANFD
  • 权值树状数组
  • 政务信息化项目建设管理办法的主要内容有哪些
  • 防火墙和网闸的区别,什么场景下,需要用到网闸?
  • iOS混淆工具有哪些?技术演进与选型趋势全景解析