当前位置:

首页 > 编程开发 > Redis之SDS数据结构如何使用

Redis之SDS数据结构如何使用

Redis Assistant
Redis Assistant

Redis Assistant 是一款为Redis设计的高效可视化管理与监控工具,提供多种操作模式,高效处理服务器数据。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

立即下载
¥19
Windows/macOS/Linux 2026-08-22
正版软件 Windows macOS软件 Linux 数据库管理工具

序言Redis的几种基本数据结构有字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(SortedSet),这些是最常见的,也能在官网上查看到。字符串前面也提到过字符串是设计了简单动态字符串SDS(SimpleDynamicString)结构来表示字符串。这种数据结构可以提升字符串的操作效率,并可以保存二进制数据。先思考一个问题:Redis是用C语言实现的,那么为什么没有复用C语言的字符串实现方法,而选用了SDS呢?char*字符串数组C语言实现字符串使用的是char*字

本文目录

      序言

      Redis的几种基本数据结构有字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set),这些是最常见的,也能在官网上查看到。

      字符串

      前面也提到过字符串是设计了简单动态字符串SDS(Simple Dynamic String)结构来表示字符串。这种数据结构可以提升字符串的操作效率,并可以保存二进制数据。

      先思考一个问题:

      Redis是用C语言实现的,那么为什么没有复用C语言的字符串实现方法,而选用了SDS呢?

      char*字符串数组

      C语言实现字符串使用的是char*字符串数组,它是一块连续的内存空间,一次存放了字符串的每一个字符,并且最后一个字符是“\0”,用来标识字符串的结尾位置,如下图,

      Redis之SDS数据结构如何使用

      连续的内存空间的所有字符串没有分隔符计算机就没办法区分字符串与字符串之间的位置。在C语言标准库中字符串的操作函数就会通过检查字符串数组中是否有“\0”来判断字符串是否结束。例如字符串操作函数strlen函数,它就是在遍历字符串数组中的每一个字符,并进行计数,直到检查到“\0”,它的时间复杂度是O(n)。流程如下,

      Redis之SDS数据结构如何使用

      简单动态字符串SDS

      SDS的数据结构里包含:字符串实际长度,字符串分配空间长度,SDS类型,字符数组,其中字符数组buf[]用来保存实际数据,如下图

      Redis之SDS数据结构如何使用

      再来看看类似的字符操作函数sdslen函数的源码(在sds.h文件中),直接根据SDS类型返回对应的字符串现有长度,避免了对字符串的遍历,时间复杂度变成了O(1),当然也会付出一点代价增加了空间复杂度。这都是设计人员让数据操作更加高效。源码如下,

      static inline size_t sdslen(const sds s) {
          unsigned char flags = s[-1];
          switch(flags&SDS_TYPE_MASK) {
              case SDS_TYPE_5:
                  return SDS_TYPE_5_LEN(flags);
              case SDS_TYPE_8:
                  return SDS_HDR(8,s)->len;
              case SDS_TYPE_16:
                  return SDS_HDR(16,s)->len;
              case SDS_TYPE_32:
                  return SDS_HDR(32,s)->len;
              case SDS_TYPE_64:
                  return SDS_HDR(64,s)->len;
          }
          return 0;
      }

      再来看一下字符串的拷贝源码,操作都使用了字符串的现有长度,拷贝后进行更新。

      sds sdscpylen(sds s, const char *t, size_t len) {
          // 判断字符串数组分配的空间长度是不是小于字符串数组当前长度
          if (sdsalloc(s) < len) {
              // 根据要追加的长度len-sdslen(s)和现有长度,判断是否增加新的空间
              s = sdsMakeRoomFor(s,len-sdslen(s));
              if (s == NULL) return NULL;
          }
          // 将源字符串t中len长度的数据拷贝到目标字符串结尾
          memcpy(s, t, len);
          // 拷贝完后,在目标字符串结尾加上\0
          s[len] = '\0';
          // 设置字符串数组最新当前长度
          sdssetlen(s, len);
          return s;
      }

      SDS把目标字符串的空间检查和扩容封装在了sdsMakeRoomFor函数中,追加、打印、复制等操作都会调用该函数。可以看到该函数根据sds的信息进行动态扩容,源码如下,

      sds sdsMakeRoomFor(sds s, size_t addlen) {
          void *sh, *newsh;
          // 获取sds可用空间
          size_t avail = sdsavail(s);
          size_t len, newlen;
          char type, oldtype = s[-1] & SDS_TYPE_MASK;
          int hdrlen;
       
          // 如果可用空间大于等于要增加的空间,则直接返回
          if (avail >= addlen) return s;
          // sds长度
          len = sdslen(s);
          // sds指针
          sh = (char*)s-sdsHdrSize(oldtype);
          // 新字符串长度
          newlen = (len+addlen);
          // 如果新长度小于最大预分配长度,则进行两倍扩容
          if (newlen < SDS_MAX_PREALLOC)
              newlen *= 2;
          else
              newlen += SDS_MAX_PREALLOC;
          type = sdsReqType(newlen);
          // SDS类型5转换为类型8
          if (type == SDS_TYPE_5) type = SDS_TYPE_8;
       
          hdrlen = sdsHdrSize(type);
          if (oldtype==type) {
              newsh = s_realloc(sh, hdrlen+newlen+1);
              if (newsh == NULL) return NULL;
              s = (char*)newsh+hdrlen;
          } else {
              /* Since the header size changes, need to move the string forward,
               * and can't use realloc */
              newsh = s_malloc(hdrlen+newlen+1);
              if (newsh == NULL) return NULL;
              memcpy((char*)newsh+hdrlen, s, len+1);
              s_free(sh);
              s = (char*)newsh+hdrlen;
              s[-1] = type;
              sdssetlen(s, len);
          }
          sdssetalloc(s, newlen);
          return s;
      }

       可以看到sdsMakeRoomFor函数中sdshdr5类型不再使用直接转换成了sdshdr8类型,它们是SDS设计的5种类型,分别表示sdshdr5、sdshdr8、sdshdr16、sdshdr32和sdshdr64,下面就看一下这几种类型的结构源码,如下图,

      struct __attribute__ ((__packed__)) sdshdr5 {
          unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
          char buf[];
      };
      struct __attribute__ ((__packed__)) sdshdr8 {
          uint8_t len; /* used */
          uint8_t alloc; /* excluding the header and null terminator */
          unsigned char flags; /* 3 lsb of type, 5 unused bits */
          char buf[];
      };
      struct __attribute__ ((__packed__)) sdshdr16 {
          uint16_t len; /* used */
          uint16_t alloc; /* excluding the header and null terminator */
          unsigned char flags; /* 3 lsb of type, 5 unused bits */
          char buf[];
      };
      struct __attribute__ ((__packed__)) sdshdr32 {
          uint32_t len; /* used */
          uint32_t alloc; /* excluding the header and null terminator */
          unsigned char flags; /* 3 lsb of type, 5 unused bits */
          char buf[];
      };
      struct __attribute__ ((__packed__)) sdshdr64 {
          uint64_t len; /* used */
          uint64_t alloc; /* excluding the header and null terminator */
          unsigned char flags; /* 3 lsb of type, 5 unused bits */
          char buf[];
      };

      sdshdr5已不再使用,所以在函数中做了处理,把sdshdr5类型转换为sdshdr8类型。前面也提到过SDS是紧凑型字符串数据结构,以sdshdr8为例,它是用的是uint8_t即8位无符号整型,会占用1字节的内存空间。SDS之所以设计不同的结构是为了能灵活保存不同大小的字符串,从而有效节省内存空间。

      另外,__attribute__ ((__packed__))标志可以告诉编译器在编译以上数据结构时,不实用字节对齐的方式(不满8字节的整数倍,则会自动补齐),而是采用紧凑的方式分配内存。

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发
      相关文章 更多
      php72w redis,docker php7安装php-redis
      php72w redis,docker php7安装php-redis

      在Docker环境下为PHP安装Redis扩展:手动编译需下载源码、安装phpize、配置编译并安装,生成redis.so并在php.d目录新建redis.ini加载,重启PHP-FPM;也可直接yum安装php72w-pecl-redis,免手动编译。

      php 如何配置 redis,php 如何设置redis
      php 如何配置 redis,php 如何设置redis

      配置PHP的Redis扩展:先安装Redis服务,从GitHub下载phpredis源码,用phpize编译安装,修改php.ini添加extension=redis.so,重启php-fpm或Apache,通过phpinfo验证并连接Redis。

      c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】
      c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】

      C++结构体无法直接序列化为RESP协议,需手动将字段映射为*N数组嵌套$lenbulkstring形式。整数需转换为字符串并加:前缀,空字符串用$0\r\n,null用$-1\r\n。长度计算需精确到字节数,特别注意二进制安全,同时确保字段顺序与结构体定义一致。

      如何在Golang架构中部署Redict作为高能数据临时存储中转站
      如何在Golang架构中部署Redict作为高能数据临时存储中转站

      Redis作为跨服务临时状态枢纽,需按消息队列、缓存和原子状态三重角色设计。选型用List实现任务分发及延迟重试,Stream用于事件广播与位点回溯。go-redis/v8需配置MinIdleConns、MaxRetries及超时。数据写入应JSON序列化并带type标签、设合理TTL、将关键字段提至顶层。消费通过原子GET+DEL或processedkey

      TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制
      TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制

      在高并发秒杀场景下,利用Redis的减库存原子操作预扣减库存拦截超卖请求,再通过数据库乐观锁(版本号与库存校验)确保最终一致性,异常时回滚Redis库存并借助Lua脚本保证原子性,从而有效防止库存超卖。

      如何在 Go 中利用 go-redis 执行 Lua 脚本优化逻辑
      如何在 Go 中利用 go-redis 执行 Lua 脚本优化逻辑

      在Go中使用go-redis执行Lua脚本可借助原子性解决竞态问题,通过redis.Script封装脚本并自动处理SHA1缓存与重试。需注意KEYS必须为非空切片且所有键在Cluster模式下落在同一slot,ARGV以字符串传入并由tonumber转换。返回值需先检查错误再调用Val(),避免panic。

      TP6.0 基于 Redis SetNX 实现的简单互斥锁机制【并发】
      TP6.0 基于 Redis SetNX 实现的简单互斥锁机制【并发】

      先看一个典型场景:电商秒杀、库存扣减、定时任务重复调度——这些并发冲突在 TP6 应用里屡见不鲜。用 Redis 的 SETNX 做互斥锁,确实是轻量又直接的方案,但“能用”和“稳妥”之间,隔着三层硬功夫:原子加锁、身份校验、合理超时。这三条,差一条就翻车。 很多人第一次上手,直接写 SETNX 加

      TP6.0 如何处理高并发抢购?Redis 原子锁与队列削峰详解【秒杀】
      TP6.0 如何处理高并发抢购?Redis 原子锁与队列削峰详解【秒杀】

      高并发抢购中,Redis的DECR原子操作可安全扣库存;配合think-queue异步削峰,接口仅扣库存和推消息;分布式锁需用SETNXEX加唯一标识,释放时用Lua脚本校验;成功资格后须以数据库事务双写为准,失败时补回Redis库存。

      Golang 实现基于 Redis 的延时任务队列
      Golang 实现基于 Redis 的延时任务队列

      基于Redis的ZSET实现延时队列需解决并发重复消费、失败重试混乱、精度不足及内存爆炸等问题。通过Lua脚本保证取删原子性,使用毫秒级时间戳,对临时错误采用指数退避重试,永久错误记入独立哈希表,并限制最大延迟、定期清理过期任务、使用紧凑序列化格式。

      RedisInsight怎么修改Key的过期时间 Redis操作方法【步骤】
      RedisInsight怎么修改Key的过期时间 Redis操作方法【步骤】

      操作 Redis 的时候,改个 Key 的过期时间其实是个高频场景。特别是做运维排查或者临时调试那会儿,谁也不想反复切命令行窗口、背那些命令参数。好在 RedisInsight 这个图形工具把这事儿做得很顺手——三种方式任选,总有一种合你胃口。 在动手前,先确认一下 Key 当前的 TTL 状态是个

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

      即将离开本站
      您即将前往第三方网站,请确认是否继续?