Redis基本数据类型:String

2024-08-13 07:48:49 浏览数 (2)

redis中的字符串底层是使用自定义的SDS数据结构。

Redis是基于C语言编写,而C语言中的字符串其实就是字符数组,它除了二进制不安全外,还缺少额外信息,无法支持一些高级特性(扩容、越界判断等),于是Redis在字符数组的基础之上做了少了变更,形成了SDS结构。

Redis3.2之前的版本中SDS都是如下结构

代码语言:javascript复制
struct sds {
    int len; // buf中已占用字节数
    int free; // buf中剩余可用字节数
    char buf[]; // 数据空间
};

对比C中的字符串,SDS的有点主要有:

  1. 额外维护了相关的空间使用信息,可以快速获取字符串长度
  2. 因为维护了字符串长度,可以不使用""作为终止字符,所以是二进制安全的

此外,SDS还将柔性数组buf的指针直接暴露,兼容了C语言处理字符串的各种函数

柔性数组(Flexible Array Member,亦称为可变长数组成员)是一种在 C99 标准引入的结构体特性。它允许结构体的最后一个元素定义为一个未指定大小的数组,这样可以在运行时动态地为这个数组分配内存。柔性数组提供了一种灵活的方法来处理变长数据结构,使得内存分配和管理更加方便。

Redis以高性能著称,而其高性能的核心就是完全的内存操作,内存是Redis的立身之本,所以Redis对于内存的使用非常精细,一个典型的例子就是SDS根据不同的数据长度又分成了5种类型,尽量用最合理的数据类型来表示额外的空间信息

一字节有8位,其中3位可以表示小于8的数据范围,5位可以表示小于32的数据范围,Redis将SDS划分成了5种类型,刚好可以用高三位表示,那么对于长度小于32的短字符串,Redis使用了一个字节的标记字段就保存了类型和长度两个信息,其结构如下

代码语言:javascript复制
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};

对于长度大于等于32的字符串,无法继续使用一个字节的标记字段表示,只能额外拿出两个字段来标记长度和容量,不同的是根据范围的不同,这两个字段使用的数据类型会有差异,其具体结构如下

代码语言:javascript复制
struct __attribute__((__packed__))sdshdr8 {
    uint8_t len; /* 已使用长度,用1字节存储 */
    uint8_t alloc; /* 总长度,用1字节存储*/
    unsigned char flags; /* 低3位存储类型,高5位预留 */
    char buf[]; /*柔性数组,存放实际内容*/
};

struct __attribute__((__packed__))sdshdr16 {
    uint16_t len; /*已使用长度,用2字节存储*/
    uint16_t alloc; /* 总长度,用2字节存储*/
    unsigned char flags; /* 低3位存储类型,高5位预留 */
    char buf[]; /*柔性数组,存放实际内容*/
};

struct __attribute__((__packed__))sdshdr32 {
    uint32_t len; /*已使用长度,用4字节存储*/
    uint32_t alloc; /* 总长度,用4字节存储*/
    unsigned char flags; /* 低3位存储类型,高5位预留 */
    char buf[]; /*柔性数组,存放实际内容*/
};

struct __attribute__((__packed__))sdshdr64 {
    uint64_t len; /*已使用长度,用8字节存储*/
    uint64_t alloc; /* 总长度,用8字节存储*/
    unsigned char flags; /* 低3位存储类型,高5位预留 */
    char buf[]; /*柔性数组,存放实际内容*/
};

不知道你看到这个结构是否会有疑惑:在使用的时候如何能区分出这几种结构呢?

这需要结合一下两点:

  • sds给上层返回的是buf[]的指针,观察上述5种结构,buf[]的前面都是flags字段,拿到flags字段自然就能得知属于那种数据类型
  • __packed__字段告知编译器1字节对齐,相当于保证flagsbuf[]是挨着的,保证我们上述的计算方式可以执行

0 人点赞