详解redis数据结构之sds

发布时间 - 2026-01-11 01:07:07    点击率:

详解redis数据结构之sds

  字符串在redis中使用非常广泛,在redis中,所有的数据都保存在字典(Map)中,而字典的键就是字符串类型,并且对于很大一部分字典值数据也是又字符串组成的。以下是sds的具体存储结构:

      从图中可以看出,sds的属性有三个:len、free和buf数组。这里len字段是用来保存sds字符串中所包含字符数目的,free字段则是用来保存buf数组中空余的部分的长度的,而buf数组则是实际用来保存字符串的。比如如下结构保存了“Hello World!”这个字符串:

      这里需要注意的是,sds和c字符串一样,需要在字符串结尾加上一个“\0”表示该字符串的结束。这里这个sds对象的len属性保存了“Hello World!”这个字符串的长度,而free属性保存了数组中空余的位数,buf数组则实际保存了这个字符串,空字符和空余位。

      redis使用sds结构而不用c字符串保存字符串的原因有如下几点:

      ①常数复杂度获取字符串长度

      通过读取sds对象的len属性的值我们可以使用O(1)获取sds对象保存的字符串长度,而在c字符串中,我们必须对整个数组进行遍历从而获取字符串的长度,其时间复杂度为O(N)。

      ②杜绝缓冲区溢出

      在c字符串中,比如char *strcat(char *dest, const char *src)函数将src连接到dest的末尾,但是c字符串假定dest数组中有足够的空余空间来保存src数组,如果dest数组长度不够就会造成缓冲区溢出;在sds对象中也提供了类似的函数sds sdscat(sds s, const char *t)和sds sdscatsds(sds s, const sds t),这两个函数在调用之前会检查目标sds对象s中free属性是否能够保存要连接的字符串的长度,如果不够,就会对目标sds对象扩容,这就保证了sds对象不会造成缓冲区溢出。

      ③减少修改字符串时内存重分配的次数

      在对sds进行修改的时候,redis可以通过“空间预分配”和“惰性空间释放”来保证后续对sds对象的频繁修改而不会造成sds对象的buf数组经常分配空间;而对于c字符串,每次对其进行修改都需要进行一次空间分配和复制操作。

      ④二进制安全

      对于c字符串,由于其判断是否结束的标志是从字符串开始到结尾碰到的第一个“\0”字符,这就限制了c字符串不能保存像图片、音频、视频、压缩文件等二进制保存的内容;而对于sds对象,由于判断其是否结束的标志是其len属性,也就是说无论在len长度内,buf数组中是否包含“\0”都不影响redis判断其是否结束。

      上面讲到了sds的空间预分配和惰性空间释放,sds通过这两种操作极大的简化了其对字符串的修改和对空间的分配工作。

      空间预分配指的是当对一个sds对象进行结构性增加时,比如修改其内容使其增长或者连接另一个字符串到其末尾,sds会预先分配一定的空间以预防未来可能对其进行的修改。如下是redis进行空间预分配的主要代码:

sds sdsMakeRoomFor(sds s, size_t addlen) {

  struct sdshdr *sh, *newsh;

  // 获取 s 目前的空余空间长度
  size_t free = sdsavail(s);

  size_t len, newlen;

  // s 目前的空余空间已经足够,无须再进行扩展,直接返回
  if (free >= addlen) return s;

  // 获取 s 目前已占用空间的长度
  len = sdslen(s);
  sh = (void*) (s-(sizeof(struct sdshdr)));

  // s 最少需要的长度
  newlen = (len+addlen);

  // 根据新长度,为 s 分配新空间所需的大小
  if (newlen < SDS_MAX_PREALLOC)
    // 如果新长度小于 SDS_MAX_PREALLOC 
    // 那么为它分配两倍于所需长度的空间
    newlen *= 2;
  else
    // 否则,分配长度为目前长度加上 SDS_MAX_PREALLOC
    newlen += SDS_MAX_PREALLOC;
  // T = O(N)
  newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);

  // 内存不足,分配失败,返回
  if (newsh == NULL) return NULL;

  // 更新 sds 的空余长度
  newsh->free = newlen - len;

  // 返回 sds
  return newsh->buf;
}

      从图中可以看出,当要添加的内容比目标sds对象的free属性要短时直接返回并将要添加的内容添加到目标sds对象的buf数组中即可;当要添加的内容比目标sds对象的free属性要长时,就会计算要添加的内容和sds对象的当前长度的和newlen,如果newlen小于SDS_MAX_PREALLOC也即1M的时候,新创建的buf数组的长度为newlen的两倍,如果newlen大于SDS_MAX_PREALLOC的时候,新创建的buf数组的长度为newlen+SDS_MAX_PREALLOC,即只多分配1M的预留空间。空间预分配保证了sds对象的空余位长度至多为扩张之后字符串长度的1倍,这也就保证了后续对sds对象的修改将尽可能少的分配空间。

      惰性空间释放指的是当对一个sds对象进行缩短操作时,其不会直接将buf数组缩短为目标数组的长度,而是只改变sds对象的len属性的值,数组中多余的部分则保存在free属性中,这样就可以保证后续可能的对该sds对象的增长操作不需要重新分配空间。

      最后需要进行说明的是,sds对象也和c一样使用“\0”作为字符串的结尾的原因是redis也是使用c语言编写的,使用“\0”结尾就可以直接使用部分c函数库中对字符串操作的函数。

      通过上面对sds对象的说明可以发现,redis对sds对象的处理极大的减少了字符串处理中可能出现的复杂操作,并且大部分操作基本上都可以在极短的时间内完成,这就保证了redis对字符串处理的高速率。

       感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# 详解redis数据结构之sds  # redis数据结构中sds  # redis  # sds  # Redis中String字符串和sdshdr结构体超详细讲解  # 几分钟教你掌握Redis简单动态字符串SDS  # Redis之SDS数据结构的使用  # 解析Redis 数据结构之简单动态字符串sds  # Redis源码阅读:Redis字符串SDS详解  # redis内部数据结构之SDS简单动态字符串详解  # Redis中SDS简单动态字符串详解  # 组中  # 这就  # 的是  # 存了  # 就会  # 则是  # 所需  # 长度为  # 对其  # 可以看出  # 指的是  # 两倍  # 图中  # 就可以  # 都不  # 第一个  # 不需要  # 遍历  # 中有  # 而在 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】  如何在 Pandas 中基于一列条件计算另一列的分组均值  成都品牌网站制作公司,成都营业执照年报网上怎么办理?  Laravel数据库迁移怎么用_Laravel Migration管理数据库结构的正确姿势  如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程  如何选择PHP开源工具快速搭建网站?  北京企业网站设计制作公司,北京铁路集团官方网站?  Laravel表单请求验证类怎么用_Laravel Form Request分离验证逻辑教程  Android自定义控件实现温度旋转按钮效果  如何在阿里云香港服务器快速搭建网站?  如何在新浪SAE免费搭建个人博客?  Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】  JavaScript实现Fly Bird小游戏  Java垃圾回收器的方法和原理总结  香港服务器租用每月最低只需15元?  大同网页,大同瑞慈医院官网?  Python函数文档自动校验_规范解析【教程】  Python高阶函数应用_函数作为参数说明【指导】  Python文件异常处理策略_健壮性说明【指导】  消息称 OpenAI 正研发的神秘硬件设备或为智能笔,富士康代工  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  jQuery 常见小例汇总  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  微信小程序 wx.uploadFile无法上传解决办法  Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】  香港服务器如何优化才能显著提升网站加载速度?  标题:Vue + Vuex + JWT 身份认证的正确实践与常见误区解析  Laravel路由怎么定义_Laravel核心路由系统完全入门指南  如何在建站宝盒中设置产品搜索功能?  黑客如何通过漏洞一步步攻陷网站服务器?  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?  如何生成腾讯云建站专用兑换码?  Laravel如何实现API速率限制?(Rate Limiting教程)  移动端脚本框架Hammer.js  Laravel Debugbar怎么安装_Laravel调试工具栏配置指南  Laravel如何保护应用免受CSRF攻击?(原理和示例)  JavaScript数据类型有哪些_如何准确判断一个变量的类型  php json中文编码为null的解决办法  在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?  C#如何调用原生C++ COM对象详解  微信小程序 scroll-view组件实现列表页实例代码  Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程  Laravel如何记录自定义日志?(Log频道配置)  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  Laravel Session怎么存储_Laravel Session驱动配置详解  phpredis提高消息队列的实时性方法(推荐)  Laravel如何与Docker(Sail)协同开发?(环境搭建教程)  Laravel如何连接多个数据库_Laravel多数据库连接配置与切换教程  UC浏览器如何设置启动页 UC浏览器启动页设置方法  Laravel如何自定义分页视图?(Pagination示例)