1.3 zipmap 结构

如果redisObject的type 成员值是 REDIS_HASH 类型的,则当该hash 的 entry 小于配置值: hash-max-zipmap-entries 或者value字符串的长度小于 hash-max-zipmap-value, 则可以编码成 REDIS_ENCODING_ZIPMAP 类型存储,以节约内存. 否则采用 Dict 来存储.

zipmap 其实质是用一个字符串数组来依次保存key和value,查询时是依次遍列每个 key-value 对,直到查到为止. 其结构示意图如下:

redis相关知识(四)_小技巧

为了节约内存,这里使用了一些小技巧来保存 key 和 value 的长度. 如果 key 或 value 的长度小于ZIPMAP_BIGLEN(254),则用一个字节来表示,如果大于ZIPMAP_BIGLEN(254),则用5个字节保存,第一个字节为保存ZIPMAP_BIGLEN(254),后面4个字节保存 key或value 的长度.

  1. 初始化时只有2个字节,第1个字节表示 zipmap 保存的 key-value 对的个数(如果key-value 对的个数超过 254,则一直用254来表示, zipmap 中实际保存的 key-value 对个数可以通过 zipmapLen() 函数计算得到).

    • hset(nick,wuzhu) 后,



    • 第1个字节保存key-value 对(即 zipmap 的entry 数量)的数量1

    • 第2个字节保存key_len 值 4

    • 第3~6 保存 key “nick”

    • 第 7 字节保存 value_len 值 5

    • 第 8 字节保存空闭的字节数 0 (当 该 key 的值被重置时,其新值的长度与旧值的长度不一定相等,如果新值长度比旧值的长度大,则 realloc 扩大内存; 如果新值长度比旧值的长度小,且相差大于 4 bytes ,则 realloc 缩小内存,如果相差小于 4,则将值往前移,并用 empty_len 保存空闲的byte 数)

    • 第 9~13字节保存 value 值 “wuzhu”

  2. hset(age,30)

    插入 key-value 对 (“age”,30)


  3. hset(nick,tide)

    插入 key-value 对 (“nick”,”tide”), 后可以看到 empty_len 为1 ,


1.4 ziplist 结构

如果redisObject的type 成员值是 REDIS_LIST 类型的,则当该list 的 elem数小于配置值: hash-max-ziplist-entries 或者elem_value字符串的长度小于 hash-max-ziplist-value, 则可以编码成 REDIS_ENCODING_ZIPLIST 类型存储,以节约内存. 否则采用 Dict 来存储.

ziplist 其实质是用一个字符串数组形式的双向链表. 其结构示意图如下:

redis相关知识(四)_小技巧_02

  1. ziplist header由3个字段组成:

    • ziplist_bytes: 用一个uint32_t 来保存, 构成 ziplist 的字符串数组的总长度,包括 ziplist header,

    • ziplist_tail_offset: 用一个uint32_t 来保存,记录 ziplist 的尾部偏移位置.

    • ziplist_length: 用一个 uint16_t 来保存,记录 ziplist 中 elem 的个数


  2. ziplist node 也由 3 部分组成:

    • prevrawlen: 保存上一个 ziplist node 的占用的字节数,包括: 保存prevarwlen,currawlen 的字节数和elem value 的字节数.

    • currawlen&encoding: 当前elem value 的raw 形式存款所需的字节数及在ziplist 中保存时的编码方式(例如,值可以转换成整数,如示意图中的”1024”, raw_len 是 4 字节,但在 ziplist 保存时转换成 uint16_t 来保存,占2 个字节).

    • (编码后的)value


可以通过 prevrawlen 和 currawlen&encoding 来遍列 ziplist.

ziplist 还能到一些小技巧来节约内存.

  • len 的存储: 如果 len 小于 ZIP_BIGLEN(254),则用一个字节来保存; 否则需要 5 个字节来保存,第 1 个字节存 ZIP_BIGLEN,作为标识符.

  • value 的存储: 如果 value 是数字类型的,则根据其值的范围转换成 ZIP_INT_16B, ZIP_INT_32B或ZIP_INT_64B 来保存,否则用 raw 形式保存.

1.5 adlist 结构



typedef struct listNode
{
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
typedef struct listIter
{
listNode *next;
int direction;
} listIter;
typedef struct list
{
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned int len;
} list;


常见的双向链表,不作分析.

1.6 intset 结构

redis相关知识(四)_示意图_03

intset 是用一个有序的整数数组来实现集合(set). struct intset 的定义如下:


typedef struct intset
{
uint32_t encoding;
uint32_t length;
int8_t contents[];
} intset;

encoding: 来标识数组是 int16_t 类型, int32_t 类型还是 int64_t 类型的数组. 至于怎么先择是那种类型的数组,是根据其保存的值的取值范围来决定的,初始化时是 int16_t, 根据 set 中的最大值在 [INT16_MIN, INT16_MAX] , [INT32_MIN, INT32_MAX], [INT64_MIN, INT64_MAX]的那个取值范围来动态确定整个数组的类型. 例如set一开始是 int16_t 类型,当一个取值范围在 [INT32_MIN, INT32_MAX]的值加入到 set 时,则将保存 set 的数组升级成 int32_t 的数组.


  • length: 表示 set 中值的个数

  • contents: 指向整数数组的指针