《閒扯Redis五》List數據類型底層之quicklist


1、前言

Redis 提供了5種數據類型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),理解每種數據類型的特色對於redis的開發和運維很是重要。html

原文解析java

Redis五種數據類型

Redis 中的 list 是咱們常用到的一種數據類型,根據使用方式的不一樣,能夠應用到不少場景中。node

2、底層解析

一、上節回顧

 上節**《閒扯Redis四》List數據類型底層編碼轉換** 說道,在 3.0 版本的 Redis 中,List 類型有兩種實現方式:redis

一、使用壓縮列表(ziplist)實現的列表對象。算法

二、使用雙端鏈表(linkedlist)實現的列表對象。數組

在 3.2 版本後新增了 quicklist 數據結構實現了 list,如今就來分析下 quicklist 的結構數據結構

二、官方描述

 先來看看 Redis 官方對 quicklist 的描述:運維

A doubly linked list of ziplists

    A generic doubly linked quicklist implementation
複製代碼

 可見 quicklist 是一個雙向鏈表,而且是一個 ziplist 的雙向鏈表,也就是說 quicklist 的每一個節點都是一個 ziplist。而經過前面的文章我們能夠知道,ziplist 自己也是一個能維持數據項前後順序的列表,並且數據項保存在一個連續的內存塊中。那是否是意味着 quicklist 結合了壓縮列表和雙端鏈表的特色呢!性能

Redis五種數據類型

三、結構分析

quicklist 結構定義

/* * quicklist */
typedef struct quicklist {
    //頭結點
    quicklistNode *head; 
    //尾節點
    quicklistNode *tail; 
    //全部ziplist中entry數量
    unsigned long count; 
    //quicklistNodes節點數量
    unsigned int len;   
    //ziplist中entry能保存的數量,由list-max-ziplist-size配置項控制 
    int fill : 16;       
    //壓縮深度,由list-compress-depth配置項控制
    unsigned int compress : 16; 
} quicklist;
複製代碼

quicklist 結構屬性註釋

註釋:ui

fill :ziplist 中 entry 能保存的數量,由 list-max-ziplist-size 配置項控制

表示了單個節點(quicklistNode)的負載比例(fill factor),負數限制 quicklistNode 中的 ziplist 的字節長度, 
    正數限制 quicklistNode 中的 ziplist 的最大長度。
複製代碼
-5: 最大存儲空間: 64 Kb <-- 一般狀況下不要設置這個值
-4: 最大存儲空間: 32 Kb <-- 很是不推薦
-3: 最大存儲空間: 16 Kb <-- 不推薦
-2: 最大存儲空間: 8 Kb <-- 推薦
-1: 最大存儲空間: 4 Kb <-- 推薦
對於正整數則表示最多能存儲到你設置的那個值, 當前的節點就裝滿了
一般在 -2 (8 Kb size) 或 -1 (4 Kb size) 時, 性能表現最好
複製代碼

compress :壓縮深度,由 list-compress-depth 配置項控制

表示 quicklist 中的節點 quicklistNode, 除開最兩端的 compress 個節點以後, 中間的節點都會被壓縮(LZF壓縮算法)。
複製代碼

quicklistNode 結構定義

typedef struct quicklistNode {
    //前節點指針
    struct quicklistNode *prev; 
    //後節點指針
    struct quicklistNode *next; 
    //數據指針。當前節點的數據沒有壓縮,那麼它指向一個ziplist結構;不然,它指向一個quicklistLZF結構。
    unsigned char *zl;
    //zl指向的ziplist實際佔用內存大小。須要注意的是:若是ziplist被壓縮了,那麼這個sz的值仍然是壓縮前的ziplist大小
    unsigned int sz;  
    //ziplist裏面包含的數據項個數
    unsigned int count : 16;   
    //ziplist是否壓縮。取值:1--ziplist,2--quicklistLZF 
    unsigned int encoding : 2; 
    //存儲類型,目前使用固定值2 表示使用ziplist存儲
    unsigned int container : 2; 
    //當咱們使用相似lindex這樣的命令查看了某一項原本壓縮的數據時,須要把數據暫時解壓,這時就設置recompress=1作一個標記,等有機會再把數據從新壓縮
    unsigned int recompress : 1;
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
複製代碼

quicklistLZF 結構定義

typedef struct quicklistLZF {
    unsigned int sz;  //壓縮後的ziplist大小
    char compressed[];//柔性數組,存放壓縮後的ziplist字節數組
} quicklistLZF;
複製代碼

四、quicklist 結構圖

 根據上述結構體定義,我們能夠繪製一下 quicklist 的結構:

Redis五種數據類型

3、要點總結

一、雙端鏈表

1.雙端鏈表便於在表的兩端進行 push 和 pop 操做,可是它的內存開銷比較大;

2.雙端鏈表每一個節點上除了要保存數據以外,還要額外保存兩個指針;

3.雙端鏈表的各個節點是單獨的內存塊,地址不連續,節點多了容易產生內存碎片;

二、壓縮列表

1.ziplist 因爲是一整塊連續內存,因此存儲效率很高;

2.ziplist 不利於修改操做,每次數據變更都會引起一次內存的 realloc;

3.當 ziplist 長度很長的時候,一次 realloc 可能會致使大批量的數據拷貝,進一步下降性能;

三、quicklist

1.空間效率和時間效率的折中;

2.結合了雙端鏈表和壓縮列表的優勢;

Redis五種數據類型
相關文章
相關標籤/搜索