redis基础数据结构(八) 压缩列表

ziplist即压缩链表,规定编码格式,通过编码可以获取内存中的内容,redis中的ziplist总体而言没有什么新技术,注意的一个细节是realloc以后内存地址可能变化,需要谨慎的使用之前保存的指针。总体设计:

<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>若没有特殊说明,所有字段都用小端存储

zlbytes:一个uint32_t,保存一个ziplist总的内存字节数,包括自己,这个字段的意义是,需要resize的时候,不需要对ziplist进行遍历

zltail:一个uint32_t,保存最后一个entry的偏移,有了这个字段可以支持pop操作而不用每次都遍历整个ziplist

zllen:一个uint16_t,保存entry数量,若超过2^16-1,那么若想获取entry数量,需要遍历整个ziplist,这种情况下这个字段要填全f

zlend:一个uint8_t,标识ziplist结束,值是255,其他entry的第一个字节不允许是255

ziplist的entry:

每个entry有两个前缀,第一个是前一个entry的长度,这样就可以支持从尾开始向前遍历,第二个是entry的编码类型,比如number或者string,若是string的话,填string的有效载荷的长度,结构如下:

<prevlen> <encoding> <entry-data>

有时候entry-data包含在encoding的一些bit中,比如一些小整数,这时候就没有entry-data字段了

<prevlen>的规则是,若上一个entry的长度比254字节小,那么只需要消耗一个字节用来表示一个8bit整数,从0到253;若比254大或者等于254,那么占用5字节,第一个字节填写0xFE表示大于等于254,后面4个字节表示上一个entry的实际长度

<encoding>的规则是,encoding的前2个bit用来决定是number还是string,若是string,那么是00,01,10,若是number,那么是11,具体规则如下:

|00pppppp|:小于等于63字节的字符串,pppppp是实际长度

|01pppppp|qqqqqqqq|:小于等于16383字节的string,注意这里是大端保存的

|10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt|:大于等于16384字节的string,10后面6个bit不使用,后面4个字节是实际的长度,这个也是大端保存的

|11000000|:表示是uint16,后面2个字节是这个整数的值,一共3个字节

|11010000|:表示是uint32,后面4个字节是这个整数的值,一共5个字节

|11100000|:表示是uint64,后面8个字节是这个整数的值,一共9个字节

|11110000|:一个24bit整数,后面3个字节是这个整数的值,一共4个字节

|11111110|:一个8bit整数,后面1个字节是这个整数的值,一共2个字节

|1111xxxx|:一个4bit整数,为了标识符不重复,xxxx只能从0001到1101,即十进制1到13,减1,得到一个0到12的范围,就是这个4bit整数能表示的范围

|11111111|:ziplist结束的标识符

和ziplist的头一样,所有整数都是小端表示,即使是在大端系统中编译的。

一个ziplist存储number的例子:

[0f 00 00 00] [0c 00 00 00] [02 00] [00 f3] [02 f6] [ff]

前4个字节是zlbytes,意思是一共15字节,接下来4个字节是zltail,意思是,最后一个entry的偏移是12,接下来2个字节是entry的个数,即2,第一个entry是00f3,00是上一个entry的长度,因为它是第一个,所以是0,f3是一个4bit整数,3-1=2,即这个整数的值是2,第二个entry是02f6,02是上一个entry的字节数,f6表示4bit整数5,最后是结束标记ff。

一个ziplist存储string的例子:

[02] [0b] [48 65 6c 6c 6f 20 57 6f 72 6c 64]:这是一个entry的内容,02表示上一个entry的长度是12,0b表示这是一个00pppppp结构的string,长度是11字节,后面跟着的11字节是string的ASCII码表示,即hello world

ziplist中提供的宏:

#define ZIP_END 255         /* Special "end of ziplist" entry. */
#define ZIP_BIG_PREVLEN 254 /* Max number of bytes of the previous entry, for
                               the "prevlen" field prefixing each entry, to be
                               represented with just a single byte. Otherwise
                               it is represented as FF AA BB CC DD, where
                               AA BB CC DD are a 4 bytes unsigned integer
                               representing the previous entry len. */

/* Different encoding/length possibilities */
#define ZIP_STR_MASK 0xc0			//string类型掩码
#define ZIP_INT_MASK 0x30			//number类型掩码
#define ZIP_STR_06B (0 << 6)		//6bit string
#define ZIP_STR_14B (1 << 6)		//14bit string
#define ZIP_STR_32B (2 << 6)		//32bit string
#define ZIP_INT_16B (0xc0 | 0<<4)	//16bit int
#define ZIP_INT_32B (0xc0 | 1<<4)	//32bit int
#define ZIP_INT_64B (0xc0 | 2<<4)	//64bit int
#define ZIP_INT_24B (0xc0 | 3<<4)	//24bit int
#define ZIP_INT_8B 0xfe				//8bit int

/* 4 bit integer immediate encoding |1111xxxx| with xxxx between
 * 0001 and 1101. */
#define ZIP_INT_IMM_MASK 0x0f   /* Mask to extract the 4 bits value. To add
                                   one is needed to reconstruct the value. */
#define ZIP_INT_IMM_MIN 0xf1    /* 11110001 */
#define ZIP_INT_IMM_MAX 0xfd    /* 11111101 */

#define INT24_MAX 0x7fffff
#define INT24_MIN (-INT24_MAX - 1)	//0x800000

/* Macro to determine if the entry is a string. String entries never start
 * with "11" as most significant bits of the first byte. */
#define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)

/* Utility macros.*/

/* Return total bytes a ziplist is composed of. */
#define ZIPLIST_BYTES(zl)       (*((uint32_t*)(zl)))

/* Return the offset of the last item inside the ziplist. */
#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl) + sizeof(uint32_t))))

/* Return the length of a ziplist, or UINT16_MAX if the length cannot be
 * determined without scanning the whole ziplist. */
#define ZIPLIST_LENGTH(zl)      (*((uint16_t*)((zl) + sizeof(uint32_t) * 2)))

/* The size of a ziplist header: two 32 bit integers for the total
 * bytes count and last item offset. One 16 bit integer for the number
 * of items field. */
#define ZIPLIST_HEADER_SIZE     (sizeof(uint32_t) * 2 + sizeof(uint16_t))

/* Size of the "end of ziplist" entry. Just one byte. */
#define ZIPLIST_END_SIZE        (sizeof(uint8_t))

/* Return the pointer to the first entry of a ziplist. */
#define ZIPLIST_ENTRY_HEAD(zl)  ((zl) + ZIPLIST_HEADER_SIZE)

/* Return the pointer to the last entry of a ziplist, using the
 * last entry offset inside the ziplist header. */
#define ZIPLIST_ENTRY_TAIL(zl)  ((zl) + intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))

/* Return the pointer to the last byte of a ziplist, which is, the
 * end of ziplist FF entry. */
#define ZIPLIST_ENTRY_END(zl)   ((zl) + intrev32ifbe(ZIPLIST_BYTES(zl)) - 1)

/* Increment the number of items field in the ziplist header. Note that this
 * macro should never overflow the unsigned 16 bit integer, since entires are
 * always pushed one at a time. When UINT16_MAX is reached we want the count
 * to stay there to signal that a full scan is needed to get the number of
 * items inside the ziplist. */
#define ZIPLIST_INCR_LENGTH(zl, incr) { \
    if (ZIPLIST_LENGTH(zl) < UINT16_MAX) \
        ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl)) + incr); \
}

ziplist.c中提供的结构体:

typedef struct zlentry {
    unsigned int prevrawlensize; /* Bytes used to encode the previos 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;

ziplist.c中提供的zpi:

ZIPLIST_ENTRY_ZERO:给一个zlentry结构清零

ZIP_ENTRY_ENCODING:获取一个entry的编码方式,是第一个字节,是number的话正好,若是string那么获取的不是全部的encoding

zipIntSize:根据一个整数的encoding返回<entry-data>部分的长度

zipStoreEntryEncoding:构建一个entry的encoding字段

ZIP_DECODE_LENGTH:从一个entry的encoding字段中解码出其<entry-data>部分的长度

zipStorePrevEntryLengthLarge:构建一个entry的prevlen字段,只有当上一个entry的长度大于等于254字节才能调用此函数

zipStorePrevEntryLength:构建一个entry的<prevlen>字段的长度,包括1字节和5字节两种情况

ZIP_DECODE_PREVLENSIZE:从一个entry中解码出<prevlen>字段的长度

ZIP_DECODE_PREVLEN:从一个entry的<prevlen>字段中解码出上一个entry的长度

zipPrevLenByteDiff:从一个entry当前内存中解码出上一个entry的长度,根据提供的当前长度,计算差值

zipRawEntryLength:从一个entry中获取这个entry的总长度,包括<prevlen><encoding><entry-data>三部分

zipTryEncoding:若一个entry可以被解码为整数,获取它的encoding和值

zipSaveInteger:构建一个整数entry的<entry-data>部分

zipLoadInteger:获取一个整数entry保存的这个整数的值

zipEntry:将一个entry的字段解析成zlentry结构体

ziplistNew:初始化一个ziplist

ziplistResize:重建一个ziplist的大小,注意这个函数会给end赋值

__ziplistCascadeUpdate:处理由于某一个entry变化导致后面所有entry的<prevlen>字段需要更新的情况,注意意图是不想让ziplist萎缩,所以即使某个entry的长度下降到254字节以下,后面的entry仍然使用5字节的<prevlen>

__ziplistDelete:删除从某个entry开始的连续若干个entry

__ziplistInsert:插入一个entry,内部函数,调用者提供<entry-data>及其长度,要插入的位置

ziplistMerge:合并两个ziplist,不支持合并自己,需要注意这个函数会导致其中较短的ziplist的内存被释放

ziplistPush:在头或者尾插入一个entry

ziplistIndex:获取第index个entry,前面编号从0开始,后面编号从-1开始

ziplistNext:返回当前entry的下一个entry

ziplistPrev:返回当前entry的上一个entry

ziplistGet:获取一个entry在<entry-data>中存储的内容

ziplistInsert:是__ziplistInsert的包装,插入一个entry

ziplistDelete:是__ziplistDelete的包装,删除一个entry

ziplistDeleteRange:从指定index开始,删除若干entry

ziplistCompare:比较一个entry中的<entry-data>中保存的值是否和给定的值相等

ziplistFind:在一个ziplist中寻找一个特定的entry,支持每次找不到的话跳过若干个entry

ziplistLen:计算一个ziplist中的entry个数

ziplistBlobLen:返回一个ziplist的总长度

ziplistRepr:此函数用于完整的打印一个ziplist的所有entry的内容

猜你喜欢

转载自blog.csdn.net/kdb_viewer/article/details/80782103
今日推荐