您的位置:首页 > 数据库 > Redis

Redis-压缩列表源码原理解析

2019-01-17 10:00 447 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zanpengfei/article/details/86518737

1、Redis为了节约内存空间,zset和hash在对象比较少的时候,采用压缩列表(ziplist)来存储,可以用过debug object key 来查看结构

1)ziplist的结构体

struct ziplist<T> {

    int32 zlbytes; // 整个压缩列表占用字节数

    int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点

    int16 zllength; // 元素个数

    T[] entries; // 元素内容列表,挨个挨个紧凑存储

    int8 zlend; // 标志压缩列表的结束,值恒为 0xFF

}

各字段排列如下图所示:

解释:压缩列表支持双向遍历,ztail_offset字段是为了快速定位到最后一个元素,然后倒着遍历

2)entry结构体

struct entry {

    int<var> prevlen; // 前一个 entry 的字节长度

    int<var> encoding; // 元素类型编码

    optional byte[] content; // 元素内容

}

解释:prevlen字段是为了从后往前遍历的时候,通过这个字段快速定位到下个元素的位置,该字段是一个变长的整数,当字符串长度小于254时,使用一个字节表示;但大于254的时候采用5个字节表示,其中第一个字节时0xFF(254),剩下四个表示长度。

encoding:元素内容存储编码格式

2、增加元素

由于ziplist是紧凑存储,没有多余的空间来添加元素,所以没次添加的时候,系统都要进行扩容,根据内存分配器算法和当前ziplist内存大小,reallloc是重新分配新的内存空间或者在原有地址上进行扩展,如果真心爱在原有地址上扩展就不需要进行旧内容的内存拷贝。

3、级联更新

源码如下:

unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p) {

    size_t curlen = intrev32ifbe(ZIPLIST_BYTES(zl)), rawlen, rawlensize;

    size_t offset, noffset, extra;

    unsigned char *np;

    zlentry cur, next;

    while (p[0] != ZIP_END) {

        zipEntry(p, &cur);

        rawlen = cur.headersize + cur.len;

        rawlensize = zipStorePrevEntryLength(NULL,rawlen);

        /* Abort if there is no next entry. */

        if (p[rawlen] == ZIP_END) break;

        zipEntry(p+rawlen, &next);

        /* Abort when "prevlen" has not changed. */

        // prevlen 的长度没有变,中断级联更新

        if (next.prevrawlen == rawlen) break;

        if (next.prevrawlensize < rawlensize) {

            /* The "prevlen" field of "next" needs more bytes to hold

             * the raw length of "cur". */

            // 级联扩展

            offset = p-zl;

            extra = rawlensize-next.prevrawlensize;

            // 扩大内存

            zl = ziplistResize(zl,curlen+extra);

            p = zl+offset;

            /* Current pointer and offset for next element. */

            np = p+rawlen;

            noffset = np-zl;

            /* Update tail offset when next element is not the tail element. */

            // 更新 zltail_offset 指针

            if ((zl+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))) != np) {

                ZIPLIST_TAIL_OFFSET(zl) =

                    intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))+extra);

            }

            /* Move the tail to the back. */

            // 移动内存

            memmove(np+rawlensize,

                np+next.prevrawlensize,

                curlen-noffset-next.prevrawlensize-1);

            zipStorePrevEntryLength(np,rawlen);

            /* Advance the cursor */

            p += rawlen;

            curlen += extra;

        } else {

            if (next.prevrawlensize > rawlensize) {

                /* This would result in shrinking, which we want to avoid.

                 * So, set "rawlen" in the available bytes. */

                // 级联收缩,不过这里可以不用收缩了,因为 5 个字节也是可以存储 1 个字节的内容的

                // 虽然有点浪费,但是级联更新实在是太可怕了,所以浪费就浪费吧

                zipStorePrevEntryLengthLarge(p+rawlen,rawlen);

            } else {

                // 大小没变,改个长度值就完事了

                zipStorePrevEntryLength(p+rawlen,rawlen);

            }

            /* Stop here, as the raw length of "next" has not changed. */

            break;

        }

    }

    return zl;

}

解释:

1)每个entry都有一个prevlen字段,而且是变长的,以253为零界点,大于253用5个字节表示,小于或者等于用1个字节,如果由253变成了254,则会发生级联更新

2)如果每个entry恰好都存储253个字节内容,那么修改第一个entry内容旧会导致后续所有的entry发生级联更新,还是比较耗费计算机资源的。

3)删除中间节点也会发生级联更新

4、IntSet小整数集合

     当set集合容纳的元素都是整数并且个数较少时,Redis会使用intSet来存储元素,intSet是紧凑数组,支持16、32、64位

1)结构体

struct intset<T> {

    int32 encoding; // 决定整数位宽是 16 位、32 位还是 64 位

    int32 length; // 元素个数

    int<T> contents; // 整数数组,可以是 16 位、32 位和 64 位

}

如果存储的是非整数,立马会更改类型,如下图所示

> sadd codehole 1 2 3

(integer) 3

> debug object codehole

Value at:0x7fec2dc2bde0 refcount:1 encoding:intset serializedlength:15 lru:6065795 lru_seconds_idle:4

> sadd codehole go java python

(integer) 3

> debug object codehole

Value at:0x7fec2dc2bde0 refcount:1 encoding:hashtable serializedlength:22 lru:6065810 lru_seconds_idle:5

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: