设为首页 加入收藏

TOP

Redis内部数据结构详解之ziplist(二)
2014-11-24 00:44:28 来源: 作者: 【 】 浏览:39
Tags:Redis 内部 数据结构 详解 ziplist
前节点数据的长度 //注意保存字符串与整数之间的差别 ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len); return prevlensize + lensize + len; }
/* Return the difference in number of bytes needed to store the length of the
 * previous element 'len', in the entry pointed to by 'p'. */
//计算需要存储len所需字节数与当前节点p的prev_entry_bytes_length的差值
static int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
    unsigned int prevlensize;
    ZIP_DECODE_PREVLENSIZE(p, prevlensize);
    return zipPrevEncodeLength(NULL, len) - prevlensize;
}

八、__ziplistCascadeUpdate、__ziplistDelete、__ziplistInsert函数详解

注;以下注释与代码中的一些字段请参照zlentry数据结构中的定义,这三个函数是ziplist一切操作的核心,尤其是在memmove进行数据移动的时候更需要多思考,在阅读下面的代码之前先阅读ziplistResize函数,而且需要对C语言中的realloc重新分配内存函数需要有一定的了解。

/**
 * 当将一个新节点添加到某个节点之前的时候,如果原节点的prevlen不足以保存新节点的长度,
 * 那么就需要对原节点的空间进行扩展(从 1 字节扩展到 5 字节)。
 *
 * 但是,当对原节点进行扩展之后,原节点的下一个节点的 prevlen 可能出现空间不足,
 * 这种情况在多个连续节点的长度都接近 ZIP_BIGLEN 时可能发生。
 *
 * 这个函数就用于处理这种连续扩展动作。
 *
 * 因为节点的长度变小而引起的连续缩小也是可能出现的,不过,为了避免扩展-缩小-扩展-缩小这样的情况反复出现(flapping,抖动),
 * 我们不处理这种情况,而是任由 prevlen 比所需的长度更长
 *
 * 复杂度:O(N^2)
 *
 * 返回值:更新后的 ziplist
 * zl: ziplist首地址,p:需要扩展prevlensize的节点首地址
 */
static 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) {
        cur = zipEntry(p);
        rawlen = cur.headersize + cur.len; //整个entry的字节数
        rawlensize = zipPrevEncodeLength(NULL,rawlen); //存储rawlen需要的字节数

        /* Abort if there is no next entry. */
        if (p[rawlen] == ZIP_END) break;// 已经到达表尾,退出
        next = zipEntry(p+rawlen);//得到下一个节点的zlentry

        /* Abort when "prevlen" has not changed. */
        // 如果下一的prevlen等于当前节点的rawlen,那么说明编码大小无需改变,退出
        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. */
            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. 这里的注释不是很清楚,需要自己思考*/
            //np+rawlensize新的下一个节点存储自身数据的首地址
            //np+next.prevrawlensize旧的下一个节点存储自身数据的首地址
            //将旧的下一个节点next的数据区到ziplist尾部全部向后偏移,空余出rawlensize个字节用来存储上个节点的长度
            memmove(np+rawlensize,
                np+next.prevrawlensize,
                curlen-noffset-next.prevrawlensize-1);
            zipPrevEncodeLength(np,rawlen);//空余出的rawlensize个字节存储上个节点的长度值

            /* Advance the cursor */
            p += rawlen; //下一个节点
            curlen += extra; //更新当前ziplist的长度
        } else {
            // 下一节点的长度编码空间有多余,不进行收缩,只是将被编码的长度写入空间
            if (next.prevrawlensize > rawlensize) {
                /* This would result in shrinking, which we want to avoid.
                 * So, set "rawlen" in the available bytes. */
                zipPrevEncodeLengthForceLarge(p+rawlen,rawlen);
            } else {
                zipPrevEncodeLength(p+rawlen,rawlen);
            }

            /* Stop here, as the raw length of "next" has not changed. */
            break;//后面的节点不用扩展
        }
    }
    return zl;
}

/* Delete "num" entries, starting at "p". Returns pointer to the ziplist. */
//从指针 p 开始,删除 num 个节点
static unsigned char *__ziplistDelete(unsigned char *zl, unsigned char *p, unsigned int num) {
    unsigned int i, totlen, deleted = 0;
    size_t offset;
    int nextdiff = 0;
    zlentry first, tail;

    first = zipEntry(p); //删除的首个节点
    for (i = 0; p[0] != ZIP_END && i < num; i++) {
        p += zipRawEntryLength(p); //偏移到下个节点
        deleted++;
    }

    totlen = p-first.p;// 被删除的节点的总字节数
    if (totlen > 0) {
        if (p[0] != ZIP_END) {
            /* Storing `prevrawlen` in this entry may increase or decrease the
             * number of bytes required compare to the current `prevrawlen`.
             * There always is room to store this, because it was previously
             * stored by an entry that is now being deleted. */
            //计算删除的第一个节点first的prevrawlensize与p节点prevrawlensize的差值
            nextdiff = zipPrevLenByteDiff(p,first.prevrawlen);
            p -= nextdiff; //根据nextdiff值,对p进行向前或向后偏移,留取的字节来保存first.prevrawlen
            zipPrevEncodeLength(p,first.prevrawlen);//将first.prevrawlen值存储在p的prevrawlensize中

            /* Update offset for tail */
            ZIPLIST_TAIL_OFFSET(zl) =
                intrev32ifbe(intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl))-totlen);

            /* When the tail contains more than one entry, we need to take
             * "nextdiff" in account as well. Otherwise, a change in t
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇MongoDB之整库备份还原单表collec.. 下一篇DbVisualizerPersonal中文乱码问..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: