Redis系列之底层数据结构SDS详解

 更新时间:2024年11月07日 09:02:32   作者:smileNicky  
SDS(简单动态字符串)是Redis使用的核心数据结构,用于替代C语言的字符串,以解决长度获取慢、内存溢出等问题,SDS通过预分配与惰性释放策略优化内存使用,增强安全性,且能存储文本与二进制数据,可查看源码src/sds.h和src/sds.c了解更多

实验的环境

  • Redis 6.0
  • VSCode 1.88.1

什么是SDS?

SDS:Simple Dynamic String,翻译为简单动态字符串。

SDS是一种用于存储二进制数据的数据结构,具有动态扩容的特点,代码位于src/sds.hsrc/sds.c

SDS的总体数据结构大致如图:在源码里sds包括几个部分,lenallocflagsbuf,其中 sdshdr是头部,buf是真实存储数据的地方,在存储的数据后面会跟一个\0,所以数据加上\0就是所谓的buf

  • len:保存了SDS字符串的长度
  • buf[]:保存数据的地方
  • alloc:分别以uint8, uint16, uint32, uint64表示整个SDS
  • flags:始终为一字节, 以低三位标示着头部的类型, 高5位未使用

查看源码sds.h,可以看到SDS里面有几种不同的头部,其中sdshdr5实际并未使用到,所以实际上有四种不同的头部

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
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[];
};

为什么要使用SDS?

Redis是用C语言写的,为什么不直接就用C语言里的char来定义字符串?

获取字符串长度

由于有len属性,所以获取SDS字符串的长度只需要读取len属性,所以时间复杂度为O(1)

如果直接使用C语言中的字符串来实现,获取字符串的长度需要遍历计数,时间复杂度为O(n)

避免缓存区溢出

C语言中,如果使用strcat函数来进行两个字符串的拼接,如果没有分配足够长度的内存空间,就会造成缓存区溢出。

而对于SDS数据类型,在进行字符串修改的时候,会根据记录的len属性检查内存空间是否满足需求,如果不满足,会进行相应空间的扩展,所以不会出现缓存区溢出

减少字符串内存重新分配次数

C语言中字符串,是不会记录字符串的长度的,所以一旦修改了字符串,就需要重新分配内存,因为如果没有重新分配,字符串长度增大时会造成内存溢出区溢出,长度减小时会造成内存泄漏。

而对于SDS来说,因为有长度熟悉lenalloc属性的存在,SDS实现了空间预分配惰性空间释放两种策略来减少重新分配内存

  1. 空间预分配:SDS对空间进行扩展的时候,扩展的内存比实际需要的多,这样可以减少字符串增长操作所需的内存重新分配次数
  2. 惰性空间释放:SDS对字符串进行缩短操作时,不会立即进行内存重新分配,来回收缩短后多余的内存空间,而是使用alloc将这些字节数量记录下来,等待后续使用

二进制安全

C语言中,是以空字符串作为字符串结束的标识,但是一些特殊的字符串,可能就包括空字符串的,所以容易丢失数据,不能正确存取。

而SDS是根据len属性,以处理二进制的方式来处理buf里的数据,所以保存数据更加安全

兼容部分C字符串函数

SDS可以重用C语言库<string.h>中的一部分函数

C字符串和SDS对比

C字符串SDS
获取字符串长度时间复杂度为O(n)获取字符串的长度时间复杂度为O(1)
不安全,可能会造成缓冲区溢出安全,不会造成缓冲区溢出
修改字符串n次就需要进行n次内存分配修改字符串长度n次,最多需要n次内存分配
只能保存文本数据可以保存文本数据或者二进制数据
可以使用所有<string.h>库中的函数可以使用一部分<string.h>库中的函数

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Windows操作系统下Redis服务安装图文教程

    Windows操作系统下Redis服务安装图文教程

    这篇文章主要介绍了Windows操作系统下Redis服务安装图文教程,文中给大家提供了redis的下载地址,安装程序步骤,需要的朋友可以参考下
    2018-03-03
  • Redis异常测试盘点分析

    Redis异常测试盘点分析

    这篇文章主要为大家介绍了Redis异常测试盘点分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • SpringBoot集成Redis的思路详解

    SpringBoot集成Redis的思路详解

    Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。接下来通过本文给大家分享SpringBoot集成Redis的详细过程,感兴趣的朋友一起看看吧
    2021-10-10
  • 压缩Redis里的字符串大对象操作

    压缩Redis里的字符串大对象操作

    这篇文章主要介绍了压缩Redis里的字符串大对象操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Redis框架在项目中的实战

    Redis框架在项目中的实战

    Lettuce是一款高性能、异步非阻塞的 Redis 客户端,基于Netty框架开发,也是Spring Boot 2.x 及以上版本默认的 Redis 客户端客户端(Lettuce)只需配置哨兵节点地址和主节点名称,本文介绍Redis框架在项目中的实战,感兴趣的朋友一起看看吧
    2026-02-02
  • redis客户端实现高可用读写分离的方式详解

    redis客户端实现高可用读写分离的方式详解

    基于sentienl 获取和动态感知 master、slaves节点信息的变化,我们的读写分离客户端就能具备高可用+动态扩容感知能力了,接下来通过本文给大家分享redis客户端实现高可用读写分离的方式,感兴趣的朋友一起看看吧
    2021-07-07
  • Ubuntu系统中Redis的安装步骤及服务配置详解

    Ubuntu系统中Redis的安装步骤及服务配置详解

    本文主要记录了Ubuntu服务器中Redis服务的安装使用,包括apt安装和解压缩编译安装两种方式,并对安装过程中可能出现的问题、解决方案进行说明,以及在手动安装时,服务器如何添加自定义服务的问题,需要的朋友可以参考下
    2024-12-12
  • Redis实现分布式锁的五种方法详解

    Redis实现分布式锁的五种方法详解

    在分布式架构中,我们同样会遇到数据共享操作问题,本文章使用Redis来解决分布式架构中的数据一致性问题,需要的小伙伴可以参考一下
    2022-06-06
  • 详解使用Redis SETNX 命令实现分布式锁

    详解使用Redis SETNX 命令实现分布式锁

    本篇文章主要介绍了详解使用Redis SETNX 命令实现分布式锁,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • 使用Redis实现API网关或单个服务的请求限流的具体代码

    使用Redis实现API网关或单个服务的请求限流的具体代码

    在微服务架构中,对 API 网关或单个服务的请求进行速率限制至关重要,以防止恶意攻击、资源滥用并确保系统的稳定性和可用性,本文将详细探讨如何利用 Redis 实现 API 网关或单个服务的请求限流,深入分析各种主流算法,需要的朋友可以参考下
    2025-07-07

最新评论