redis源码 【sds】【redis源码】

redis string 源码解析

具体可以参考 redis 源码 sds.h 这个文件

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
/* 所有的结构体中:
 * len      表示已使用长度
 * alloc    表示可使用长度
 * flags    低 3 位保存结构类型,高 5 位未被使用 */
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[];
};

#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)

/* 返回指定 sds 已用的长度 */
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;
}

这个 是 Redis 设计了简单动态字符串(Simple Dynamic String,SDS)的结构

相比于 C 语言中的字符串实现,SDS 这种字符串的实现方式,会提升字符串的操
作效率,并且可以用来保存二进制数据。
redis 的 字符串 如果直接 用 char* 来 实现 ,会有很多问题, 比如字符串 用 末尾\0 会截断字符串, 很多 如 strlen 的 api都不能用了。

而这就不符合 Redis 希望能保存任意二进制数据的需求了。

SDS 结构设计

首先,SDS 结构里包含了一个字符数组 buf[],用来保存实际数据。同时,SDS 结构里还
包含了三个元数据,分别是字符数组现有长度 len、分配给字符数组的空间长度 alloc,以
及 SDS 类型 flags。其中,Redis 给 len 和 alloc 这两个元数据定义了多种数据类型,进
而可以用来表示不同类型的 SDS,稍后我会给你具体介绍。下图显示了 SDS 的结构,你可
以先看下

在这里插入图片描述

typedef char *sds;

Redis 使用 typedef给 char* 类型定义了一个别名,这个别名就是 sds

和 C 语言中的字符串操作相比,SDS 通过记录字符数组的使用
长度和分配空间大小,避免了对字符串的遍历操作,降低了操作开销,进一步就可以帮助
诸多字符串操作更加高效地完成,比如创建、追加、复制、比较等

sds 定义了 如 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64 ,这 5
种类型的主要区别就在于,它们数据结构中的字符数组现有长度 len 和分配空间长度
alloc,这两个元数据的数据类型不同。

SDS 之所以设计不同的结构头(即不同类型,入 sdshdr32,64等),是为了能灵活保存不同大小的字
符串,从而有效节省内存空间

我们可以看到,在 struct 和 sdshdr8 之间使
用了__attribute__ ((__packed__)) ,其实这里 的作用就是告诉编译器,在编译
sdshdr8 结构时,不要使用字节对齐的方式,而是采用紧凑的方式分配内存 。
默认情况下,编译器会按照 8 字节对齐的方式,给变量分配内存。也就是说,即使一个变
量的大小不到 8 个字节,编译器也会给它分配 8 个字节。 【禁止使用字节对齐,也是可以节省了部分的内存】
如果你在开发程序时,希望能节省数据结构的内存开销,就可以把
__attribute__ ((__packed__))这个编程方法用起来。

sds 总结

Redis 专门设计了 SDS 数据结构,在字符数组
的基础上,增加了字符数组长度和分配空间大小等元数据
而且,SDS 不通过字符串中的“\0”字符判断字符串结束,而是直接将其作为二进制数据
处理,可以用来保存图片等二进制数据。

设计不同 SDS 类型来表示不同大小的字符串,并使用__attribute__ ((__packed__)) 这个编程小技巧,来实现紧凑型内存布局,达到节省内存的目的。

char* 的不足

char* 的不足:

  • 操作效率低:获取长度需遍历,O(N)复杂度
  • 二进制不安全:无法存储包含 \0 的数据

Redis设计sds的意图:
1、满足存储传输二进制的条件(避免\0歧义)
2、高效操作字符串(通过len和alloc,快速获取字符长度大小以及跳转到字符串末尾)
3、紧凑型内存设计(按照字符串类型,len和alloc使用不同的类型节约内存,并且关闭
内存对齐来达到内存高效利用,在redis中除了sds,intset和ziplist也有类似的目底)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值