redis中quicklist浅析

前面我们分析了redis中的ziplist相关实现,在结构上一个链表结构,但是在底层用的是一整块内存,在数据量较小的时候,其查找和分配都比较快,但是当数据量比较大的时候,查找和分配就比较慢了。
因此,redis提供了quicklist数据结构,

quicklist的声明如下:

typedef struct quicklist {
    quicklistNode *head; // 
    quicklistNode *tail;
    unsigned long count;        /* total count of all entries in all ziplists */
    unsigned long len;          /* number of quicklistNodes */
    int fill : QL_FILL_BITS;              /* fill factor for individual nodes */
    unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
    unsigned int bookmark_count: QL_BM_BITS;
    quicklistBookmark bookmarks[];
} quicklist;

typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl;
    unsigned int sz;             /* ziplist size in bytes */
    unsigned int count : 16;     /* count of items in ziplist */
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    unsigned int recompress : 1; /* was this node previous compressed? */
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;


typedef struct quicklistEntry {
    const quicklist *quicklist;
    quicklistNode *node;
    unsigned char *zi;
    unsigned char *value;
    long long longval;
    unsigned int sz;
    int offset;
} quicklistEntry;

quicklist底层是基于ziplist的,其中count表示的是quicklist中所有的ziplist的entry数量之和,而len表示的quicklistNode 之和。
默认可以通过quicklistCreate创建一个空的quicklist:

quicklist *quicklistCreate(void) {
    struct quicklist *quicklist;

    quicklist = zmalloc(sizeof(*quicklist));
    quicklist->head = quicklist->tail = NULL;
    quicklist->len = 0;
    quicklist->count = 0;
    quicklist->compress = 0;
    quicklist->fill = -2;
    quicklist->bookmark_count = 0;
    return quicklist;
}

默认情况下,quicklist的容量大小为quicklist->fill = -2; 65534。

void quicklistPush(quicklist *quicklist, void *value, const size_t sz,
                   int where) {
    if (where == QUICKLIST_HEAD) {
        quicklistPushHead(quicklist, value, sz);
    } else if (where == QUICKLIST_TAIL) {
        quicklistPushTail(quicklist, value, sz);
    }
}

插入数据quicklistPush来进行插入。我们研究下是如何插入到尾结点中的:

int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {
    quicklistNode *orig_tail = quicklist->tail;
    assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */
    if (likely(
            _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) {
        quicklist->tail->zl =
            ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL);
        quicklistNodeUpdateSz(quicklist->tail);
    } else {
        quicklistNode *node = quicklistCreateNode();
        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL);

        quicklistNodeUpdateSz(node);
        _quicklistInsertNodeAfter(quicklist, quicklist->tail, node);
    }
    quicklist->count++;
    quicklist->tail->count++;
    return (orig_tail != quicklist->tail);
}
```
1. 这里首先会判断当前quicklist的tail尾结点是否还有容量进行插入,如果有的话,那么会新建一个ziplist并插入到当前尾结点的zl代表的ziplist的尾部。
2. 如果当前quicklist的tail尾结点没有容量或者尾结点是空,那么先会创建一个`quicklistNode`,同时创建一个新的ziplist,将当前节点的zl指向该ziplist节点。(如果是要加入到头结点,ziplist中也是加入到头结点)

`quicklistNodeUpdateSz`会更新`quicklistNode`的`sz`字段,也就是该node下跌ziplist的字节长度

如果是新插入一个quicklistNode节点,并且前一个节点存在(这时候表明前一个节点已经到达了最大的容量),这时候会对前一个节点进行压缩,将前一个节点`quicklistNode`的zl(ziplist)转换为:quicklistLZF结构:
```
typedef struct quicklistLZF {
    unsigned int sz; /* LZF size in bytes*/
    char compressed[];
} quicklistLZF;
```
通过`lzf_compress`方法,将节点中的ziplist进行压缩。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值