Redis基础(三)压缩列表

159 阅读2分钟

存储结构

ziplist1.png

说明

  1. zlbytes: 压缩列表的字节长度,占4个字节,因此压缩列表最多有2^32 -1个字节
  2. zltail: 压缩列表尾元素相对于压缩列表起始地址的偏移量,占4个字节
  3. zllen: 压缩列表的元素个数,占用两个字节。zllen无法存储超过65535(2^16-1)的压缩列表,必须遍历整个压缩列表才能获取到元素个数
  4. entryX: 压缩列表存储的元素,可以是字节数组或者整数,长度不限。
  5. zlend: 压缩列表的结尾,占1个字节,恒为0XFF

宏定义实现压缩列表各个字段的存取操作

ziplist1.png

问题

zlentry

对于压缩列表的任意元素,获取前一个元素的长度、判断存储的数据类型、获取数据内容都需要经过复杂的解码运算。

/* We use this function to receive information about a ziplist entry.
 * Note that this is not how the data is actually encoded, is just what we
 * get filled by a function in order to operate more easily. */
typedef struct zlentry {
    unsigned int prevrawlensize; /* Bytes used to encode the previous entry len*/
    unsigned int prevrawlen;     /* Previous entry len. */
    unsigned int lensize;        /* Bytes used to encode this entry type/len.
                                    For example strings have a 1, 2 or 5 bytes
                                    header. Integers always use a single byte.*/
    unsigned int len;            /* Bytes used to represent the actual entry.
                                    For strings this is just the string length
                                    while for integers it is 1, 2, 3, 4, 8 or
                                    0 (for 4 bit immediate) depending on the
                                    number range. */
    unsigned int headersize;     /* prevrawlensize + lensize. */
    unsigned char encoding;      /* Set to ZIP_STR_* or ZIP_INT_* depending on
                                    the entry encoding. However for 4 bits
                                    immediate integers this can assume a range
                                    of values and must be range-checked. */
    unsigned char *p;            /* Pointer to the very start of the entry, that
                                    is, this points to prev-entry-len field. */
} zlentry;

如何遍历压缩列表?

对于任意一个元素,我们如何判断其存储的是什么类型?

如何获取字节的长度

操作

创建

#define intrev32ifbe(v) (v)

/* Create a new empty ziplist. */
unsigned char *ziplistNew(void) {
    unsigned int bytes = ZIPLIST_HEADER_SIZE+ZIPLIST_END_SIZE;
    unsigned char *zl = zmalloc(bytes);
    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);
    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
    ZIPLIST_LENGTH(zl) = 0;
    zl[bytes-1] = ZIP_END;
    return zl;
}

插入

插入步骤

  1. 元素内容编码
    1. 计算previous_entry_length
    2. 计算encoding字段
    3. 计算content字段
  2. 重新分配空间
  3. 复制数据
/* Insert an entry at "p". */
unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
    return __ziplistInsert(zl,p,s,slen);
}

创建



创建