文档章节

Redis源码阅读笔记-压缩列表结构

Jian_Ming
 Jian_Ming
发布于 09/16 16:08
字数 4059
阅读 7
收藏 0

压缩列表

《Redis设计与实现》

在Redis中,压缩列表(ziplist)是由一段有特定编码的内存块构成的列表,目的是为了节约内存。

压缩列表被用作列表键和哈希键的底层实现之一。

当一个列表键只包含少量列表项,并且每个列表项要么是小整数值,要么是长度比较短的字符串,Redis会使用压缩列表作为列表键的底层实现。

当一个哈希键只包含少量键值对,且每个键值对的键和值要么是小整数值,要么是长度比较短的字符串,Redis会使用压缩列表作为哈希键的底层实现。

特点

  • 压缩列表是一种为节约内存而开发的顺序型数据结构。
  • 压缩列表被用作列表键和哈希键的底层实现之一。
  • 压缩列表可以包含多个节点,每个节点可以保存一个字节数组或者整数值。
  • 添加新节点到压缩列表,或者从压缩列表中删除节点,可能会引发连锁更新操作,但这种操作出现的几率并不高。

结构

压缩列表的结构

属性类型长度
zlbytesuint32_t4 bytes记录整个压缩列表的内存字节数。
zltailuint32_t4 bytes记录压缩列表的起始地址距离压缩列表尾结点多少字节数。使得无须遍历即可访问列表尾。
zllenuint16_t2 bytes记录压缩列表中节点的数量。当zllen的值小于UINT_16_MAX(65536)时,该属性的值就是列表节点的数量。当zllen的值等于UINT_16_MAX(65536)时,只有遍历才能统计出节点的数量。
entryX列表节点不定压缩裂变的节点,节点长度由内存决定。
zlenduint8_t1 byte标记压缩列表结束的特殊值(0xFF)。

压缩列表节点的结构

压缩列表可以保存:

  • 一个字节数组(可以为以下3种长度其中1种):
    • 长度小于等于63(2^6 - 1)字节的字节数组;
    • 长度小于等于16383(2^14 - 1)字节的字节数组;
    • 长度小于等于4294967295(2^32 - 1)字节的字节数组;
  • 一个整数值(可以为以下6种长度其中1种):
    • 4位长,介于0~12之间的无符号整数;
    • 1字节长的有符号整数;
    • 3字节长的有符号整数;
    • int16_t类型整数;
    • int32_t类型整数;
    • int64_t类型整数;

previous_entry_length

节点的previous_entry_length属性是以字节为单位,记录前一个节点的长度。

previous_entry_length属性长度可以是1字节或者5字节

  • 如果前一节点的长度小于254字节,那么previous_entry_length属性长度为1字节;前一字节的长度就保存在这一个字节中;
  • 如果前一节点的长度大于等于254字节,那么previous_entry_length属性的长度为5字节。其中属性的第1字节会被设置为0xFE(十进制254),而之后的4个字节则用于保存前一节点的长度。

encoding

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

  • 字节数组编码:
编码编码长度content属性保存的值
00bbbbbb1 byte保存长度小于等于63字节的字节数组(以00开头)
01bbbbbb xxxxxxxx2 bytes保存长度小于等于16383字节的字节数组(以01开头)
10bbbbbb xxxxxxxx aaaaaaaa cccccccc dddddddd5 bytes保存长度小于等于4294 967 295字节的字节数组(以10开头)
  • 整数编码(整数编码以11开头,占 1 byte):
编码编码长度content属性保存的值
1100 00001 byteint16_t类型的整数
1101 00001 byteint32_t类型的整数
1110 00001 byteint64_t类型的整数
1111 00001 byte24位有符号整数
1111 11101 byte8位有符号整数
1111 xxxx1 bytexxxx将是介于00011101之间,表示 0 - 12 之间的值,所以使用这一编码无须content属性

content

content用于保存节点的数据,可以使一个字节数组或者整数,类型由encoding决定。

连锁更新

当对压缩列表添加/删除节点时,为了让每个节点的previous_entry_length属性都符合压缩列表对节点的要求,需要不断对压缩列表执行空间的重新分配操作,知道末尾节点位置。这种特殊情况下产生的连续多次空间扩展操作称为连续更新

连锁更新 在最坏的情况下需要对压缩列表执行N次空间(N为节点数)重新分配操作,而每次空间重新分配的最坏复杂度为O(N),所以连续更新的最坏复杂度为O(N^2)

但是连锁更新造成性能问题的几率很低:

  • 压缩列表要恰好有多个连续的、长度介于250字节至253字节之间的节点,连锁更新才有可能被引发。
  • 即使出现连锁更新,只要被更新的节点数量不多,就不会对性能造成任何影响(压缩列表也是用于少量节点的情况下)。

部分代码解析

  • unsigned char *ziplistNew(void) 创建一个压缩列表:

    	/* Create a new empty ziplist. */
    	unsigned char *ziplistNew(void) {
    	    // ZIPLIST_HEADER_SIZE 是压缩列表固定的头部大小 = 4 bytes + 4 bytes + 2 bytes
    	    // 1 是 1 byte 的列表结束位
    	    // 所以这个是空压缩列表所需要的内存大小
    	    unsigned int bytes = ZIPLIST_HEADER_SIZE+1;
    	    unsigned char *zl = zmalloc(bytes);
    	    // 将压缩列表的 `bytes` 属性值 设为 11
    	    ZIPLIST_BYTES(zl) = intrev32ifbe(bytes);
    	    // 空压缩列表的 `zltail` 的值 为 10
    	    ZIPLIST_TAIL_OFFSET(zl) = intrev32ifbe(ZIPLIST_HEADER_SIZE);
    	    // 节点数量 `zllen` 为 0
    	    ZIPLIST_LENGTH(zl) = 0;
    	    // 将 `zlend` 值设为 255
    	    zl[bytes-1] = ZIP_END;
    	    return zl;
    	}
    
  • unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) 将长度为slen的字节数组或者整数s插入到压缩列表zl的给定节点p之后:

    	/* 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);
    	}
    
    	/* Insert item at "p". */
    	// 将长度为`slen`的字节数组或者整数`s`插入到压缩列表`zl`的位置`p`上
    	unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen) {
    	    // 获取当前压缩列表zl的元素个数 curlen
    	    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;
    
    	    // 整段代码主要为了找到待插入位置的前一个节点长度 prevlen
    	    /* Find out prevlen for the entry that is inserted. */
    	    if (p[0] != ZIP_END) {
    	        // 如果节点p不是结束位置
    	        // 获取节点p的 `previous_entry_length`属性 所占用字节数 prevlensize
    	        // 获取节点p的 `previous_entry_length`属性 的值 prevlen
    	        ZIP_DECODE_PREVLEN(p, prevlensize, prevlen);
    	    } else {
    	        // 如果节点p是结束位置
    	        // 获取最后一个节点的位置ptail
    	        unsigned char *ptail = ZIPLIST_ENTRY_TAIL(zl);
    
    	        if (ptail[0] != ZIP_END) {
    	            // 如果ptail不是结束位置,则说明压缩列表中有其他节点,插入位置为末尾
    	            // 获取节点ptail的 `previous_entry_length`属性 的值 prevlen
    	            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. */
    	    // 还需要加上 前一个节点的长度 zipStorePrevEntryLength(NULL,prevlen)
    	    // 和当前节点的长度 zipStoreEntryEncoding(NULL,encoding,slen)
    	    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. */
    	    // 当插入位置不是末尾的时候,需要保证下一个节点的`previous_entry_length`有足够的长度来保存
    	    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. */
    	    // 保存偏移量,以防realloc()改变地址
    	    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) {
    	        // 当插入位置不为末尾的时候
    
    	        // 将p之后的所有内容向后移动到p+reqlen
    	        /* 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. */
    	        // 将节点的长度写入下一个节点的`previous_entry_length`中
    	        if (forcelarge)
    	            // 需要扩展的保存写入
    	            zipStorePrevEntryLengthLarge(p+reqlen,reqlen);
    	        else
    	            zipStorePrevEntryLength(p+reqlen,reqlen);
    
    	        /* Update offset for tail */
    	        // 更新zltail的值
    	        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
    	    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;
    	}
    
    	/* Return the total number of bytes used by the entry pointed to by 'p'. */
    	unsigned int zipRawEntryLength(unsigned char *p) {
    	    unsigned int prevlensize, encoding, lensize, len;
    	    ZIP_DECODE_PREVLENSIZE(p, prevlensize);
    	    ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
    	    return prevlensize + lensize + len;
    	}
    
    	/* Check if string pointed to by 'entry' can be encoded as an integer.
    	 * Stores the integer value in 'v' and its encoding in 'encoding'. */
    	int zipTryEncoding(unsigned char *entry, unsigned int entrylen, long long *v, unsigned char *encoding) {
    	    long long value;
    
    	    if (entrylen >= 32 || entrylen == 0) return 0;
    	    if (string2ll((char*)entry,entrylen,&value)) {
    	        /* Great, the string can be encoded. Check what's the smallest
    	         * of our encoding types that can hold this value. */
    	        if (value >= 0 && value <= 12) {
    	            *encoding = ZIP_INT_IMM_MIN+value;
    	        } else if (value >= INT8_MIN && value <= INT8_MAX) {
    	            *encoding = ZIP_INT_8B;
    	        } else if (value >= INT16_MIN && value <= INT16_MAX) {
    	            *encoding = ZIP_INT_16B;
    	        } else if (value >= INT24_MIN && value <= INT24_MAX) {
    	            *encoding = ZIP_INT_24B;
    	        } else if (value >= INT32_MIN && value <= INT32_MAX) {
    	            *encoding = ZIP_INT_32B;
    	        } else {
    	            *encoding = ZIP_INT_64B;
    	        }
    	        *v = value;
    	        return 1;
    	    }
    	    return 0;
    	}
    
    	/* Return bytes needed to store integer encoded by 'encoding'. */
    	unsigned int zipIntSize(unsigned char encoding) {
    	    switch(encoding) {
    	    case ZIP_INT_8B:  return 1;
    	    case ZIP_INT_16B: return 2;
    	    case ZIP_INT_24B: return 3;
    	    case ZIP_INT_32B: return 4;
    	    case ZIP_INT_64B: return 8;
    	    }
    	    if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX)
    	        return 0; /* 4 bit immediate */
    	    panic("Invalid integer encoding 0x%02X", encoding);
    	    return 0;
    	}
    
    	/* Encode the length of the previous entry and write it to "p". Return the
    	 * number of bytes needed to encode this length if "p" is NULL. */
    	unsigned int zipStorePrevEntryLength(unsigned char *p, unsigned int len) {
    	    if (p == NULL) {
    	        return (len < ZIP_BIG_PREVLEN) ? 1 : sizeof(len)+1;
    	    } else {
    	        if (len < ZIP_BIG_PREVLEN) {
    	            p[0] = len;
    	            return 1;
    	        } else {
    	            return zipStorePrevEntryLengthLarge(p,len);
    	        }
    	    }
    	}
    
    	/* Write the encoidng header of the entry in 'p'. If p is NULL it just returns
    	 * the amount of bytes required to encode such a length. Arguments:
    	 *
    	 * 'encoding' is the encoding we are using for the entry. It could be
    	 * ZIP_INT_* or ZIP_STR_* or between ZIP_INT_IMM_MIN and ZIP_INT_IMM_MAX
    	 * for single-byte small immediate integers.
    	 *
    	 * 'rawlen' is only used for ZIP_STR_* encodings and is the length of the
    	 * srting that this entry represents.
    	 *
    	 * The function returns the number of bytes used by the encoding/length
    	 * header stored in 'p'. */
    	unsigned int zipStoreEntryEncoding(unsigned char *p, unsigned char encoding, unsigned int rawlen) {
    	    unsigned char len = 1, buf[5];
    
    	    if (ZIP_IS_STR(encoding)) {
    	        /* Although encoding is given it may not be set for strings,
    	         * so we determine it here using the raw length. */
    	        if (rawlen <= 0x3f) {
    	            if (!p) return len;
    	            buf[0] = ZIP_STR_06B | rawlen;
    	        } else if (rawlen <= 0x3fff) {
    	            len += 1;
    	            if (!p) return len;
    	            buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f);
    	            buf[1] = rawlen & 0xff;
    	        } else {
    	            len += 4;
    	            if (!p) return len;
    	            buf[0] = ZIP_STR_32B;
    	            buf[1] = (rawlen >> 24) & 0xff;
    	            buf[2] = (rawlen >> 16) & 0xff;
    	            buf[3] = (rawlen >> 8) & 0xff;
    	            buf[4] = rawlen & 0xff;
    	        }
    	    } else {
    	        /* Implies integer encoding, so length is always 1. */
    	        if (!p) return len;
    	        buf[0] = encoding;
    	    }
    
    	    /* Store this length at p. */
    	    memcpy(p,buf,len);
    	    return len;
    	}
    
    	/* Given a pointer 'p' to the prevlen info that prefixes an entry, this
    	 * function returns the difference in number of bytes needed to encode
    	 * the prevlen if the previous entry changes of size.
    	 *
    	 * So if A is the number of bytes used right now to encode the 'prevlen'
    	 * field.
    	 *
    	 * And B is the number of bytes that are needed in order to encode the
    	 * 'prevlen' if the previous element will be updated to one of size 'len'.
    	 *
    	 * Then the function returns B - A
    	 *
    	 * So the function returns a positive number if more space is needed,
    	 * a negative number if less space is needed, or zero if the same space
    	 * is needed. */
    	int zipPrevLenByteDiff(unsigned char *p, unsigned int len) {
    	    unsigned int prevlensize;
    	    ZIP_DECODE_PREVLENSIZE(p, prevlensize);
    	    return zipStorePrevEntryLength(NULL, len) - prevlensize;
    	}
    
    	/* Resize the ziplist. */
    	unsigned char *ziplistResize(unsigned char *zl, unsigned int len) {
    	    zl = zrealloc(zl,len);
    	    ZIPLIST_BYTES(zl) = intrev32ifbe(len);
    	    zl[len-1] = ZIP_END;
    	    return zl;
    	}
    
    	/* Encode the length of the previous entry and write it to "p". This only
    	 * uses the larger encoding (required in __ziplistCascadeUpdate). */
    	int zipStorePrevEntryLengthLarge(unsigned char *p, unsigned int len) {
    	    if (p != NULL) {
    	        p[0] = ZIP_BIG_PREVLEN;
    	        memcpy(p+1,&len,sizeof(len));
    	        memrev32ifbe(p+1);
    	    }
    	    return 1+sizeof(len);
    	}
    
    	/* When an entry is inserted, we need to set the prevlen field of the next
    	 * entry to equal the length of the inserted entry. It can occur that this
    	 * length cannot be encoded in 1 byte and the next entry needs to be grow
    	 * a bit larger to hold the 5-byte encoded prevlen. This can be done for free,
    	 * because this only happens when an entry is already being inserted (which
    	 * causes a realloc and memmove). However, encoding the prevlen may require
    	 * that this entry is grown as well. This effect may cascade throughout
    	 * the ziplist when there are consecutive entries with a size close to
    	 * ZIP_BIG_PREVLEN, so we need to check that the prevlen can be encoded in
    	 * every consecutive entry.
    	 *
    	 * Note that this effect can also happen in reverse, where the bytes required
    	 * to encode the prevlen field can shrink. This effect is deliberately ignored,
    	 * because it can cause a "flapping" effect where a chain prevlen fields is
    	 * first grown and then shrunk again after consecutive inserts. Rather, the
    	 * field is allowed to stay larger than necessary, because a large prevlen
    	 * field implies the ziplist is holding large entries anyway.
    	 *
    	 * The pointer "p" points to the first entry that does NOT need to be
    	 * updated, i.e. consecutive fields MAY need an update. */
    	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. */
    	        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. */
    	            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. */
    	                zipStorePrevEntryLengthLarge(p+rawlen,rawlen);
    	            } else {
    	                zipStorePrevEntryLength(p+rawlen,rawlen);
    	            }
    
    	            /* Stop here, as the raw length of "next" has not changed. */
    	            break;
    	        }
    	    }
    	    return zl;
    	}
    
    	/* Store integer 'value' at 'p', encoded as 'encoding' */
    	void zipSaveInteger(unsigned char *p, int64_t value, unsigned char encoding) {
    	    int16_t i16;
    	    int32_t i32;
    	    int64_t i64;
    	    if (encoding == ZIP_INT_8B) {
    	        ((int8_t*)p)[0] = (int8_t)value;
    	    } else if (encoding == ZIP_INT_16B) {
    	        i16 = value;
    	        memcpy(p,&i16,sizeof(i16));
    	        memrev16ifbe(p);
    	    } else if (encoding == ZIP_INT_24B) {
    	        i32 = value<<8;
    	        memrev32ifbe(&i32);
    	        memcpy(p,((uint8_t*)&i32)+1,sizeof(i32)-sizeof(uint8_t));
    	    } else if (encoding == ZIP_INT_32B) {
    	        i32 = value;
    	        memcpy(p,&i32,sizeof(i32));
    	        memrev32ifbe(p);
    	    } else if (encoding == ZIP_INT_64B) {
    	        i64 = value;
    	        memcpy(p,&i64,sizeof(i64));
    	        memrev64ifbe(p);
    	    } else if (encoding >= ZIP_INT_IMM_MIN && encoding <= ZIP_INT_IMM_MAX) {
    	        /* Nothing to do, the value is stored in the encoding itself. */
    	    } else {
    	        assert(NULL);
    	    }
    	}
    

压缩列表API

参考之《Redis设计与实现》

函数作用
unsigned char *ziplistNew(void)创建一个新的压缩列表
unsigned char *ziplistMerge(unsigned char **first, unsigned char **second)将两个压缩列表拼接一起,first的后面接着second
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where)创建一个长度为slen给定值为s的新节点,并将这个新节点添加到压缩列表的表头或者表尾
unsigned char *ziplistIndex(unsigned char *zl, int index)返回压缩列表zl给定索引index上的节点
unsigned char *ziplistNext(unsigned char *zl, unsigned char *p)返回给定节点的下一个节点
unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p)返回给定节点的前一个节点
unsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval)获取给定节点所保存的值
unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen)将长度为slen的字节数组或者整数s插入到压缩列表zl的给定节点p之后
unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p)从压缩列表zl中删除给定节点p
unsigned char *ziplistDeleteRange(unsigned char *zl, int index, unsigned int num)删除研所列表zl在给定索引index上连续num个结点
unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen)比较节点ps是否相同
unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip)在压缩裂变中查找并返回包含给定值的节点
unsigned int ziplistLen(unsigned char *zl)返回压缩列表中包含的节点个数
size_t ziplistBlobLen(unsigned char *zl)返回压缩列表占用的内存字节数

© 著作权归作者所有

共有 人打赏支持
Jian_Ming

Jian_Ming

粉丝 14
博文 26
码字总数 60229
作品 0
珠海
程序员
私信 提问
Redis 开源文档《Redis设计与实现》

Redis是运用比较广泛的NoSQL产品之一,目前的稳定版本是2.6.10,包括Github、Instagram、Blizzard、新浪微博等都在产品中大量使用了Redis。其代码基于BSD协议开源,整个项目代码量只有2万多行...

三桂sg
2013/03/14
6.8K
21
Redis源码阅读笔记-快速列表

快速列表 快速列表(quicklist)是由压缩列表(ziplist)组成的一个双向链表,链表中,每一个节点都是以压缩列表(ziplist)的结构保存。 在 Redis3.2 后加入的新数据结构,在列表键中取代了双向链...

Jian_Ming
09/20
0
0
Redis基础笔记(一)

Redis基础笔记 Redis基础笔记 事务 SORT 生存时间 任务队列 发布/订阅模式 Python中使用Redis 实际实例 管理 其他 1. 字符串类型 2. 散列类型 3. 列表类型 4. 集合类型 5. 有序集合 简介 安装...

Airship
2016/01/28
15
0
Redis 哈希结构内存模型剖析

本文共 1231字,阅读大约需要 5分钟 ! --- 概述 在前文《Redis字符串类型内部编码剖析》之中已经剖析过 Redis最基本的 String类型的内部是怎么编码和存储的,本文再来阐述 Redis中使用 最为...

CodeSheep
08/27
0
0
database

存储过程高级篇 讲解了一些存储过程的高级特性,包括 cursor、schema、控制语句、事务等。 数据库索引与事务管理 本篇文章为对数据库知识的查缺补漏,从索引,事务管理,存储过程,触发器,一...

掘金官方
01/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

从源码入手,一文带你读懂Spring AOP面向切面编程

之前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的完全了解Java动态代理吗?看这篇就够了》介绍了下JDK的动态代理。 基于这两者的实现上...

公众号_Zack说码
18分钟前
4
0
maven 常用命令

mvn deploy -Dmaven.test.skip=true mvn source:jar deploy -Dmaven.test.skip=true mvn dependency:tree -Doutput=1.txt...

yzzzzzzzz
19分钟前
1
0
JavaScript之Promise对象

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。 Pr...

前端攻城老湿
20分钟前
4
0
mysql事务,select for update,及数据的一致性处理

在MySQL的InnoDB中,预设的Tansaction isolation level 为REPEATABLE READ(可重读) 在select 的读取锁主要分为两种方式 select .... lock in share mode select ..... for update   这两...

细节探索者
22分钟前
2
0
python 将txt文件转换成excel

emmm,作为一个小白,不会的东西真的太多了,这两天好头大啊!加油坚持吧! #file_affilication = open('Affiliations.txt','r')import xlwtimport os import sysdef txt_xls(...

BellaYu
27分钟前
5
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部