《閒扯Redis三》Redis五種資料型別之List型
阿新 • • 發佈:2020-04-02
------
##一、前言
>**Redis** 提供了5種資料型別:String(字串)、Hash(雜湊)、List(列表)、Set(集合)、Zset(有序集合),理解每種資料型別的特點對於redis的開發和運維非常重要。
![Redis五種資料型別][2]
**Redis** 中的 list 是我們經常使用到的一種資料型別,根據使用方式的不同,可以應用到很多場景中。
##二、操作命令
**List資料型別在 Redis 中的相關命令:**:
|命令|描述|用法|
| -------- | :---- | :---- |
|LPUSH|1.將一個或多個值value插入到列表key的表頭
2.如果有多個value值,那麼各個value值按從左到右的順序依次插入表頭
3.key不存在,一個空列表會被建立並執行LPUSH操作
4.key存在但不是列表型別,返回錯誤|LPUSH key value [value ...]| |LPUSHX|1.將值value插入到列表key的表頭,當且僅當key存在且為一個列表
2.key不存在時,LPUSHX命令什麼都不做|LPUSHX key value| |LPOP|1.移除並返回列表key的頭元素|LPOP key| |LRANGE|1.返回列表key中指定區間內的元素,區間以偏移量start和stop指定
2.start和stop都以0位底
3.可使用負數下標,-1表示列表最後一個元素,-2表示列表倒數第二個元素,以此類推
4.start大於列表最大下標,返回空列表
5.stop大於列表最大下標,stop=列表最大下標|LRANGE key start stop| |LREM|1.根據count的值,移除列表中與value相等的元素
2.count>0表示從頭到尾搜尋,移除與value相等的元素,數量為count
3.count<0表示從從尾到頭搜尋,移除與value相等的元素,數量為count
4.count=0表示移除表中所有與value相等的元素|LREM key count value| |LSET|1.將列表key下標為index的元素值設為value
2.index引數超出範圍,或對一個空列表進行LSET時,返回錯誤|LSET key index value| |LINDEX|1.返回列表key中,下標為index的元素|LINDEX key index| |LINSERT|1.將值value插入列表key中,位於pivot前面或者後面
2.pivot不存在於列表key時,不執行任何操作
3.key不存在,不執行任何操作|LINSERT key BEFORE|AFTER pivot value| |LLEN|1.返回列表key的長度
2.key不存在,返回0|LLEN key| |LTRIM|1.對一個列表進行修剪,讓列表只返回指定區間內的元素,不存在指定區間內的都將被移除|LTRIM key start stop| |RPOP|1.移除並返回列表key的尾元素|RPOP key| |RPOPLPUSH| 在一個原子時間內,執行兩個動作:
1.將列表source中最後一個元素彈出並返回給客戶端
2.將source彈出的元素插入到列表desination,作為destination列表的頭元素|RPOPLPUSH source destination| |RPUSH|1.將一個或多個值value插入到列表key的表尾|RPUSH key value [value ...]| |RPUSHX|1.將value插入到列表key的表尾,當且僅當key存在並且是一個列表
2.key不存在,RPUSHX什麼都不做|RPUSHX key value| **實踐**:別偷懶,動手一下,try it out![Redis五種資料型別][3]
##三、應用場景
>1、lpush+lpop=Stack(棧)
>2、lpush+rpop=Queue(佇列)
>3、lpush+ltrim=Capped Collection(有限集合)
>4、lpush+brpop=Message Queue(訊息佇列)
>5、排行榜,資料最新列表等等![Redis五種資料型別][4]
##四、底層解析
結構圖上顯示,List型別有兩種實現方式:
舉例說明,建立列表物件 numbers![Redis五種資料型別][5]
###1、使用壓縮列表(ziplist)實現的列表物件
結構如下![Redis五種資料型別][6]
###2、使用雙端連結串列(linkedlist)實現的列表物件
結構如下![Redis五種資料型別][7]
##五、疑問思考
壓縮列表與雙端連結串列是什麼樣的結構?
###1、壓縮列表(ziplist)
壓縮列表(ziplist)是Redis為了節省記憶體而開發的,是由一系列特殊編碼的連續記憶體塊組成的順序型資料結構,一個壓縮列表可以包含任意多個節點(entry),每個節點可以儲存一個位元組陣列或者一個整數值。
結構如下![Redis五種資料型別][8]
壓縮列表的每個節點構成如下![Redis五種資料型別][9]
1)**previous_entry_ength**:以位元組為單位,記錄了壓縮列表中前一個位元組的長度。previous_entry_ength 的長度可以是1位元組或者5位元組:
如果前一節點的長度小於254位元組,那麼 previous_entry_ength 屬性的長度為1位元組,前一節點的長度就儲存在這一個位元組裡面。
如果前一個節點的長度大於等於254,那麼 previous_entry_ength 屬性的長度為5位元組,其中屬性的第一位元組會被設定為0xFE(十進位制254),而之後的四個位元組則用於儲存前一節點的長度。
利用此原理即當前節點位置減去上一個節點的長度即得到上一個節點的起始位置,壓縮列表可以從尾部向頭部遍歷,這麼做很有效地減少了記憶體的浪費。
2)**encoding**:記錄了節點的 content 屬性所儲存資料的型別以及長度。
3)**content**:儲存節點的值,節點的值可以是一個位元組陣列或者整數,值的型別和長度由節點的 encoding 屬性決定。
###2、雙端連結串列(linkedlist)
連結串列是一種常用的資料結構,C 語言內部是沒有內建這種資料結構的實現,所以Redis自己構建了連結串列的實現。
連結串列節點定義:
```c
typedef struct listNode{
//前置節點
struct listNode *prev;
//後置節點
struct listNode *next;
//節點的值
void *value;
}listNode
```
多個 listNode 可以通過 prev 和 next 指標組成雙端連結串列,結構如下![Redis五種資料型別][10]
另外Redis還提供了操作連結串列的資料結構:
```c
typedef struct list{
//表頭節點
listNode *head;
//表尾節點
listNode *tail;
//連結串列所包含的節點數量
unsigned long len;
//節點值複製函式
void (*free) (void *ptr);
//節點值釋放函式
void (*free) (void *ptr);
//節點值對比函式
int (*match) (void *ptr,void *key);
}list;
```
list結構為連結串列提供了表頭指標 head ,表尾指標 tail 以及連結串列長度計數器 len ,dup、free、match 成員則是用於實現多型連結串列所需的型別特定函式。
**Redis連結串列實現的特性**:
> * 雙端:連結串列節點帶有 prev 和 next 指標,獲取某個節點的前置節點和後置節點複雜度都是O(1)。
> * 無環:表頭節點的 prev 指標和表尾節點的 next 指標都指向 NULL,對連結串列的訪問以NULL為終點。
> * 帶表頭指標和表尾指標:通過list結構的 head 和 tail 指標,程式獲取連結串列的表頭節點和表尾結點的複雜度都是O(1)。
> * 帶連結串列長度計數器:程式使用 list 結構的 len屬性對 list持有的連結串列節點進行計數,程式獲取連結串列中節點數量的複雜度為O(1)。
> * 多型:連結串列節點使用 void* 指標來儲存節點值,並且通過 list 結構的 dup、 free、match 三個屬性為節點值設定型別特定函式,所以連結串列可以用於儲存各種不同型別的值。
**疑問**:Redis列表什麼時候會使用 ziplist 編碼,什麼時候又會使用 linkedlist 編碼呢?下節再說...![大道七哥,有趣話不多][12]
[1]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-1.jpg
[2]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-2.png
[3]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-3.png
[4]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-4.png
[5]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-5.png
[6]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-6.png
[7]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-7.png
[8]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-8.png
[9]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-9.png
[10]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-10.png
[11]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-11.jpg
[12]: https://img2018.cnblogs.com/blog/675297/201909/675297-20190902100403256-127012580.png
2.如果有多個value值,那麼各個value值按從左到右的順序依次插入表頭
3.key不存在,一個空列表會被建立並執行LPUSH操作
4.key存在但不是列表型別,返回錯誤|LPUSH key value [value ...]| |LPUSHX|1.將值value插入到列表key的表頭,當且僅當key存在且為一個列表
2.key不存在時,LPUSHX命令什麼都不做|LPUSHX key value| |LPOP|1.移除並返回列表key的頭元素|LPOP key| |LRANGE|1.返回列表key中指定區間內的元素,區間以偏移量start和stop指定
2.start和stop都以0位底
3.可使用負數下標,-1表示列表最後一個元素,-2表示列表倒數第二個元素,以此類推
4.start大於列表最大下標,返回空列表
5.stop大於列表最大下標,stop=列表最大下標|LRANGE key start stop| |LREM|1.根據count的值,移除列表中與value相等的元素
2.count>0表示從頭到尾搜尋,移除與value相等的元素,數量為count
3.count<0表示從從尾到頭搜尋,移除與value相等的元素,數量為count
4.count=0表示移除表中所有與value相等的元素|LREM key count value| |LSET|1.將列表key下標為index的元素值設為value
2.index引數超出範圍,或對一個空列表進行LSET時,返回錯誤|LSET key index value| |LINDEX|1.返回列表key中,下標為index的元素|LINDEX key index| |LINSERT|1.將值value插入列表key中,位於pivot前面或者後面
2.pivot不存在於列表key時,不執行任何操作
3.key不存在,不執行任何操作|LINSERT key BEFORE|AFTER pivot value| |LLEN|1.返回列表key的長度
2.key不存在,返回0|LLEN key| |LTRIM|1.對一個列表進行修剪,讓列表只返回指定區間內的元素,不存在指定區間內的都將被移除|LTRIM key start stop| |RPOP|1.移除並返回列表key的尾元素|RPOP key| |RPOPLPUSH| 在一個原子時間內,執行兩個動作:
1.將列表source中最後一個元素彈出並返回給客戶端
2.將source彈出的元素插入到列表desination,作為destination列表的頭元素|RPOPLPUSH source destination| |RPUSH|1.將一個或多個值value插入到列表key的表尾|RPUSH key value [value ...]| |RPUSHX|1.將value插入到列表key的表尾,當且僅當key存在並且是一個列表
2.key不存在,RPUSHX什麼都不做|RPUSHX key value| **實踐**:別偷懶,動手一下,try it out