重点内容:
1.String类型的内存布局
先介绍几个Redis底层数据结构,是如何支撑String类型的实现。从中可以了解到哪部分内存属于额外的内存开销。1.1 SDS结构体
SDS结构体:简单动态字符串,包含如下字段:
字段说明如下:
字段 | 描述 | 备注 |
len | buf的内存使用字节数,字段占4B。 | 非实际数据,额外开销 |
alloc | buf的内存分配字节数,字段占4B,一般大于len。 | 非实际数据,额外开销 |
buf | 字节数组,保存实际数据,C风格字符串(结尾’\0’) |
除SDS结构体中的额外开销,还有RedisObject结构体的额外开销,Redis用它来记录String,List,Hash,Set,Sorted Set等数据类型的元数据(如最后一次访问的时间、被引用的次数等),同时指向实际数据。
1.2 RedisObject结构体
RedisObject结构体,包含如下字段:
字段说明如下:
字段 | 描述 | 备注 |
元数据 | 8B | 非实际数据,额外开销 |
指针 | 8B,指向具体数据类型的实际数据 | 是否额外开销,视编码方式而定 |
1.3 dictEntry结构体
Redis采用全局哈希表(链式哈希表)保存所有键值对,表元素是一个dictEntry结构体,用来指向一个键值对。每个键值对,Redis都会在底层分配一个dictEntry结构体。该结构体有3个指针字段,用于指向key、value以及下一个dictEntry。
Redis使用的内存分配库jemalloc,在分配内存时,根据申请的字节数N,找一个比N大且最接近N的2的幂次数作为分配空间,如此可以减少频繁分配的次数。因此,dictEntry结构体会被分配32B。
1.4 编码方式
Redis针对String类型,专门设计3种编码方式来优化内存布局,从而节省内存开销。如下:
保存对象 | 编码方式 | 存储优化 |
long类型整数 | int编码 | RedisObject的指针字段用于存储数据,不再额外指向实际的数据,有效利用指针的内存开销 |
字符串数据(<=44B) | embstr编码 | RedisObject结构体和SDS结构体的内存布局是连续的,避免产生内存碎片 |
字符串数据(>44B) | raw编码 | SDS结构体的buf数据量较多,分配单独的内存空间,RedisObject结构体的指针字段指向SDS结构体。 |
3种编码方式示意图如下: