


新介绍的3大类型(bitmap、hyperLogLog:实质String | GEO:实质Zset)








127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> type hello
string
127.0.0.1:6379> object encoding hello
"embstr"
127.0.0.1:6379> debug object hello
Value at:0x7fa41f80e3a0 refcount:1 encoding:embstr serializedlength:6 lru:891806 lru_seconds_idle:61
127.0.0.1:6379>
为了便于操作,Redis采用redisObjec结构来统一五种不同的数据类型,这样所有的数据类型就都可以以相同的形式在函数间传递而不用使用特定的类型结构。同时,为了识别不同的数据类型, redisObjec中定义了type和encoding字段对不同的数据类型加以区别。简单地说,redisObjec就是string、hash、list、set、zset的父类 ,可以在函数间传递时隐藏具体的类型信息,所以作者抽象了redisObjec结构来到达同样的目的
struct redisObject {
/*对象的类型,包括:OBJECT_STRING、OBJECT_LIST、OBJECT_HASH、OBJECT_SET、OBJECT_ZSET */
unsigned type:4;
/*具体的数据结构*/
unsigned encoding:4;
/*24位,对象最后一次被命令程序访问的时间,与内存回收有关*/
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
/*引用计数,当refcount=0的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了*/
int refcount;
/*指向对象实际的数据结构*/
void *ptr;
};



代表embstr格式的SDS(Simple Dynamic String 简单动态字符串),保存长度小于44字节的字符串。EMBSTR顾名思义即:embedded string,表示嵌入式的String
保存长度大于44字节的字符串
C语言中字符串展示

Redis没有直接复用C语言的字符串,而是新建了属于自己的结构-----SDS

# redis 3.2 以前
struct sdshdr {
int len;
int free;
char buf[];
};
Redis为什么重新设计一个SDS数据结构?
提供内存预分配,避免频繁内存分配
# redis 3.2 以前
struct sdshdr {
int len;
int free;
char buf[];
};
# 1. 比如我现在set name tang
struct sdshdr {
int len = 4
int free = 0
char buf[]= [tang]
};
# 2. append name zhi
# 预分配规则: [4(tang) + 3(zhi)] * 2 = 14长度
struct sdshdr {
int len = 7
int free = 7
char buf[]= [tangzhi]
};
# 3. append name 123
# 这个时候在2时候已经预分配了14 > [4(tang) + 3(zhi) + 3(123)],不需要再额外分配
struct sdshdr {
int len = 10
int free = 4
char buf[]= [tangzhi123]
};

redis3.2以前和之后的变化,为什么要有这些变化情况存在?- redis最终理念内存
在3.2以前,假设,我们的buf[]很小,特别小,int len是用4个字节进行存储的,太浪费了
针对于这种特别小的情况,3.2之后优化了代码,使用1个字节的flags,中的5位来表示长度,也就是2^5表示长度,达到内存优化的目的
# redis 3.2 以前
struct sdshdr {
int len;
int free;
char buf[];
};
# redis 3.2 后
1. len:记录buf数组中已使用字节数量
2. flags:当前类型(不同的sdshdr)
3. alloc:不包括头和空结束符的字节数量
typedef **char** *sds;
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 {
static inline char sdsReqType(size_t string_size) {
if (string_size < 32)
return SDS_TYPE_5;
if (string_size < 0xff) //2^8 -1
return SDS_TYPE_8;
if (string_size < 0xffff) // 2^16 -1
return SDS_TYPE_16;
if (string_size < 0xffffffff) // 2^32 -1
return SDS_TYPE_32;
return SDS_TYPE_64;
}



当字符串键值的内容可以用一个64位有符号整形来表示时,Redis会将键值转化为long型来进行存储,此时即对应 OBJ_ENCODING_INT 编码类型。内部的内存结构表示如下:
命令示例: set k1 123

Redis启动时会预先建立10000个分别存储0 - 9999的redisObject变量作为共享对象,这就意味着如果set字符串的键值在0~10000之间的话,则可以直接指向共享对象而不需要再建立新对象,此时键值不占空间
set k1 123
set k2 123
redis源代码:object.c


对于长度小于44的字符串,Redis对键值采用OBJ_ENCODING_EMBSTR方式,EMBSTR顾名思义即:embedded string,表示嵌入式的String。从内存结构上来讲即字符串sds结构体与其对应的 redisObject对象分配在同一块连续的内存空间,字符串sds嵌入在redisObject对象之中一样


redis源代码:object.c(embStr不会重新再次开辟空间,而是会嵌入在redisObject里面)

当字符串的键值为长度大于44的超长字符串时,Redis则会将键值的内部编码方式改为OBJ_ENCODING_RAW格式,这与OBJ_ENCODING_EMBSTR编码方式的不同之处在于,此时动态字符串sds的内存与其依赖的redisObject的内存不再连续了

明明没有超过阈值,为什么变成raw了?
embstr与raw类型底层的数据结构其实都是SDS(简单动态字符串,Redis内部定义sdshdr一种结构)


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。