Redis的列表對象筆記

列表對象

列表數據類型是Redis裏很是經常使用的類型,當咱們想用一個鍵關聯一組對象時就能夠用列表數據類型來存儲。列表的功能並不複雜,如今就來看看Redis是怎麼實現列表功能的。html

列表的編碼

列表的編碼一共有三種:node

  • OBJ_ENCODING_ZIPLIST 壓縮列表
  • OBJ_ENCODING_QUICKLIST 編碼爲ziplist的快速列表
  • OBJ_ENCODING_LINKEDLIST 再也不使用的舊列表,使用雙端鏈表

其中OBJ_ENCODING_LINKEDLIST編碼以下面代碼所示,已經被標記爲不使用了。bash

#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
複製代碼

OBJ_ENCODING_ZIPLIST編碼雖然有定義建立函數,可是我下載了源碼而後全文搜索也沒找到哪裏有調用這個函數,也已經不使用了。數據結構

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

OBJ_ENCODING_ZIPLIST

關於ziplist的實現能夠查看筆記函數

OBJ_ENCODING_LINKEDLIST

Redis在版本3.2以前列表的底層編碼有OBJ_ENCODING_LINKEDLISTOBJ_ENCODING_ZIPLIST兩種,OBJ_ENCODING_LINKEDLIST是在元素比較大且數量比較多的狀況下使用的。ui

/* Node, List, and Iterator are the only data structures used currently. */

typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

typedef struct list {
    listNode *head;
    listNode *tail;
    void *(*dup)(void *ptr);
    void (*free)(void *ptr);
    int (*match)(void *ptr, void *key);
    unsigned long len;
} list;
複製代碼

能夠看到OBJ_ENCODING_LINKEDLIST定義了一個listlistNode結構。list結構引用了首尾兩個節點,這樣就能夠快速的在首尾編輯節點。listNode結構引用了先後兩個節點,實現了雙端鏈表。this

OBJ_ENCODING_QUICKLIST

總共三種編碼,有兩種編碼沒有用到,剩下的是就是列表的主角編碼OBJ_ENCODING_LINKEDLIST了。quicklist的定義以下:編碼

/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist. * 'count' is the number of total entries. * 'len' is the number of quicklist nodes. * 'compress' is: -1 if compression disabled, otherwise it's the number * of quicklistNodes to leave uncompressed at ends of quicklist. * 'fill' is the user-requested (or default) fill factor. */
typedef struct quicklist {
    quicklistNode *head; //指向列表的頭節點
    quicklistNode *tail; //指向列表的尾節點
    unsigned long count; // 存儲的元素總和 
    unsigned long len;   // 節點的數量 
    int fill : 16;       // 節點的大小設置 
    unsigned int compress : 16; // 節點壓縮深度設置;0=off;1表示quicklist兩端各有一個節點不壓縮,中間的節點壓縮
} quicklist;
複製代碼

看起來和OBJ_ENCODING_LINKEDLISTlist沒什麼區別,重點在於quicklistNode了。spa

/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist. * We use bit fields keep the quicklistNode at 32 bytes. * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k). * encoding: 2 bits, RAW=1, LZF=2. * container: 2 bits, NONE=1, ZIPLIST=2. * recompress: 1 bit, bool, true if node is temporarry decompressed for usage. * attempted_compress: 1 bit, boolean, used for verifying during testing. * extra: 10 bits, free for future use; pads out the remainder of 32 bits */
typedef struct quicklistNode {
    struct quicklistNode *prev; // 前一個節點
    struct quicklistNode *next; // 後一個節點
    unsigned char *zl;          // 節點存儲的值指針
    unsigned int sz;            // ziplist的大小
    unsigned int count : 16;    // 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;
複製代碼

能夠看到quicklistNode最神祕的就是存儲值的結構了,quicklistNode默認使用ziplist實現。下面是quicklist添加元素的解析:.net

/* Add new entry to head node of quicklist.
 *
 * Returns 0 if used existing head.
 * Returns 1 if new head created. */
int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {
    # 首先取出舊頭節點
    quicklistNode *orig_head = quicklist->head;
    # 判斷舊頭節點的ziplist加上新元素後的大小是否超過限制,這個限制取決於quicklist結構的fill字段
    if (likely(
            _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {
        # 沒有超過限制就直接將元素加入到舊頭節點的ziplist中
        quicklist->head->zl =
            ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);
        quicklistNodeUpdateSz(quicklist->head);
    } else {
        # 超過限制就新建一個node
        quicklistNode *node = quicklistCreateNode();
        # 元素加入到新節點的ziplist中
        node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);

        quicklistNodeUpdateSz(node);
        # 併成爲新的頭節點
        _quicklistInsertNodeBefore(quicklist, quicklist->head, node);
    }
    # 更新元素數量
    quicklist->count++;
    quicklist->head->count++;
    # 返回是否有新節點建立
    return (orig_head != quicklist->head);
}
複製代碼

結論

Redis在版本3.2以前分別在兩種狀況下使用OBJ_ENCODING_LINKEDLISTOBJ_ENCODING_ZIPLIST兩種編碼。OBJ_ENCODING_LINKEDLIST操做簡單複雜度低可是內存佔用高,OBJ_ENCODING_ZIPLIST則正好相反。後面Redis就實現了OBJ_ENCODING_QUICKLIST編碼,融合了OBJ_ENCODING_LINKEDLISTOBJ_ENCODING_ZIPLIST兩種編碼的優點,成爲了列表的惟一指定編碼。

參考資料

【Redis源碼剖析】 - 淺談Redis內置數據結構之壓縮列表ziplist

Redis源碼剖析--快速列表quicklist

Redis源碼剖析--quicklist

相關文章
相關標籤/搜索