分析的为redis现在的最新版 6.2.3
源码链接:
sds.h: https://github.com/redis/redis/blob/unstable/src/sds.h
sds.c: https://github.com/redis/redis/blob/unstable/src/sds.c
// sds的定义 typedef char *sds; /* 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; // 字符串长度,buf已经用过的长度 uint8_t alloc; // 字符串的总容量 unsigned char flags; // 第三位保存类型标志 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的类型定义:
typedef char *sds;
所以这里为什么sds
竟然等同于 char*
?sds和传统的c语言字符串保持类型兼容,因此他们的定义是一样的,都是char*。这有些情况下,需要传入一个C语言字符串的地方,也确实可以传入一个sds。但是,sds和 char * 并不等同。sds是Binary Safe
的,它可以存储任意二进制数据,不像C语言字符串那样以字符\0
来标识字符串的结束,因此它必然有个长度字段。但是这个字段在哪?实际上还有一个header结构:
sds结构体从4.0开始就使用了5种header定义,节省内存的使用,但是不会用到sdshdr5。之所以有五种类型,就是为了节省内存:
sds5
对应1<<5
32sds8
对应 1<<8
256sds16
对应 1<<16
65536sds32
对应 1<<32
2^32sds64
对应 1<<16
2^64一个sds字符串的完整结构,由在内存地址上前后相邻的两部分组成:
#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
sds的数据结构,我们非常有必要非常仔细的去解析它
上图是一个内部的结构例子,画的很丑