redis中的ziplist

1、结构

zlbytes:4字节,记录整个压缩列表占用的内存字节数

zltail:4字节,记录压缩列表表尾节点距离压缩列表的起始地址有多少字节。即entryN在ziplist中的偏移

zllen:2字节,记录了压缩列表包含的节点数,当这个属性的值小于UINT16_MAX时,这个属性的值就是压缩列表包含节点的数量 ,当这个值等于UINT16_MAX时,节点的真实数量需要遍历整个压缩列表才能计算得出。

zlend:1字节,特殊值0xff,用于标记压缩列表的末端。

压缩列表节点可以保存一个字节串或者一个整数。

 每个压缩节点的组成

previous_entry_length以字节为单位,记录了压缩列表中前一个节点的长度。取值可以是1字节或者5字节。

如果前一节点的长度小于254字节,那么previous_entry_length长度为1字节,前一节点的长度就保存在这一个字节里。

如果前一节点的长度大于等于254字节,那么previous_entry_length长度为5字节,其中第一字节设置为0xFE,之后的四字节用于保存前一节点的长度。

一字节、两字节或者五字节长,值 的最高位为00,01或者10的是字节数组编码,这种编码表示节点的content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录。

一字节长,值的最高位以11开头的是整数编码,这种编码表示节点的content属性保存的是整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录。

encoding记录节点的content属性所保存数据的类型以及长度

字符串支持的编码形式有

长度 编码 说明
<=64

|00pppppp|

pppppp表示字符串长度
<=16383

|01pppppp|qqqqqqqq|

p表示字符串长度,大端字符串存储 
>=16384

|10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt|

|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt|表示字符口中长度,大端字符序存储 

整数支持的编码形式有

长度 编码 说明
2字节

|11000000|

总共占用3字节(encoding+content)
4字节

|11010000|

总共占用5字节
8字节

|11100000|

总共占用9字节
3字节

|11110000|

总共占用4字节
1字节

|11111110|

总共占用2字节
0-12

|1111xxxx|

实际存储是的[1-13]

content保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定。

2、创建

createZiplistObject来创建ziplist

robj *createZiplistObject(void) {
    unsigned char *zl = ziplistNew();
    robj *o = createObject(OBJ_LIST,zl);
    o->encoding = OBJ_ENCODING_ZIPLIST;
    return o;
}

3、添加entry

ziplistPush来添加entry,内部是调用__ziplistInsert,在位置p前插入entry(s, slen),会更新p的prelen,ziplist的zltail,zlbytes, zllen

unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {
    unsigned char *p;
    p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
    return __ziplistInsert(zl,p,s,slen);
}

unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), reqlen;
    unsigned int prevlensize, prevlen = 0;
    size_t offset;
    int nextdiff = 0;
    unsigned char encoding = 0;
    long long value = 123456789; /* initialized to avoid warning. Using a value
                                    that is easy to see if for some reason
                                    we use it uninitialized. */
    zlentry tail;

    /* Find out prevlen for the entry that is inserted. */
    if (p[0] != ZIP_END) {
        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
    } else {
        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
        if (ptail[0] != ZIP_END) {
            prevlen = zipRawEntryLength(ptail);
        }
    }

    /* See if the entry can be encoded */
    if (zipTryEncoding(s,slen,&value,&encoding)) {
        /* 'encoding' is set to the appropriate integer encoding */
        reqlen = zipIntSize(encoding);
    } else {
        /* 'encoding' is untouched, however zipStoreEntryEncoding will use the
         * string length to figure out how to encode it. */
        reqlen = slen;
    }
    /* We need space for both the length of the previous entry and
     * the length of the payload. */
    reqlen += zipStorePrevEntryLength(NULL,prevlen);
    reqlen += zipStoreEntryEncoding(NULL,encoding,slen);

    /* When the insert position is not equal to the tail, we need to
     * make sure that the next entry can hold this entry's length in
     * its prevlen field. */
    int forcelarge = 0;
    nextdiff = (p[0] != ZIP_END) ? zipPrevLenByteDiff(p,reqlen) : 0;
    if (nextdiff == -4 && reqlen < 4) {
        nextdiff = 0;
        forcelarge = 1;
    }

    /* Store offset because a realloc may change the address of zl. */
    offset = p-zl;
    zl = ziplistResize(zl,curlen+reqlen+nextdiff);
    p = zl+offset;

    /* Apply memory move when necessary and update tail offset. */
    if (p[0] != ZIP_END) {
        /* Subtract one because of the ZIP_END bytes */
        memmove(p+reqlen,p-nextdiff,curlen-offset-1+nextdiff);

        /* Encode this entry's raw length in the next entry. */
        if (forcelarge)
            zipStorePrevEntryLengthLarge(p+reqlen,reqlen);
        else
            zipStorePrevEntryLength(p+reqlen,reqlen);

        /* Update offset for tail */
        ZIPLIST_TAIL_OFFSET(zl) =
            intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+reqlen);

        /* When the tail contains more than one entry, we need to take
         * "nextdiff" in account as well. Otherwise, a change in the
         * size of prevlen doesn't have an effect on the *tail* offset. */
        zipEntry(p+reqlen, &tail);
        if (p[reqlen+tail.headersize+tail.len] != ZIP_END) {
            ZIPLIST_TAIL_OFFSET(zl) =
                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+nextdiff);
        }
    } else {
        /* This element will be the new tail. */
        ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(p-zl);
    }

    /* When nextdiff != 0, the raw length of the next entry has changed, so
     * we need to cascade the update throughout the ziplist */
    if (nextdiff != 0) {
        offset = p-zl;
        zl = __ziplistCascadeUpdate(zl,p+reqlen);
        p = zl+offset;
    }

    /* Write the entry */
    p += zipStorePrevEntryLength(p,prevlen);
    p += zipStoreEntryEncoding(p,encoding,slen);
    if (ZIP_IS_STR(encoding)) {
        memcpy(p,s,slen);
    } else {
        zipSaveInteger(p,value,encoding);
    }
    ZIPLIST_INCR_LENGTH(zl,1);
    return zl;
}

 redis中的hash当使用ziplist来存储key,value时,其内部entry列表顺序为key1,value1,..., keyn,valuen

你可能感兴趣的:(redis,redis,数据库,database)