jtoken判断是否包含键_Redis列表键(linkedlist/ziplist)的介绍
Redis的列表對象(list object)底層實現(xiàn)之一就是鏈表。
當(dāng)列表對象可以同時滿足以下倆個條件時,列表對象使用ziplist編碼:
* 列表對象保存的所有字符串元素的長度都小于64字節(jié);
* 列表對象保存的元素數(shù)量小于512個;不能呢滿足這兩個條件的列表需要使用linkedlist編碼。
鏈表鏈表在redis中的使用:除了鏈表鍵之外,發(fā)布與訂閱、慢查詢、監(jiān)視器等功能也用到了鏈表,Redis 服務(wù)器
本身還使用鏈表來保存多個客戶端的狀態(tài)信息,以及使用鏈表來構(gòu)建客戶端輸出緩沖區(qū)
(output buffer)
- 鏈表和鏈表節(jié)點的實現(xiàn):
listNode 結(jié)構(gòu):
typedef struct listNode { // 前置節(jié)點 struct listNode * prev; // 后置節(jié)點 struct listNode * next; // 節(jié)點的值 void * value; } listNode;
list 結(jié)構(gòu):
typedef struct list { // 表頭節(jié)點 listNode * head; // 表尾節(jié)點 listNode * tail; // 鏈表所包含的節(jié)點數(shù)量 unsigned long len; // 節(jié)點值復(fù)制函數(shù) void * ( * dup)(void * ptr); // 節(jié)點值釋放函數(shù) void ( * free)(void * ptr); // 節(jié)點值對比函數(shù) int ( * match)(void * ptr,void * key); } list;
list 結(jié)構(gòu)為鏈表提供了表頭指針 head、表尾指針 tail,以及鏈表長度計數(shù)器 len,
而 dup、free 和 match 成員則是用于實現(xiàn)多態(tài)鏈表所需的類型特定函數(shù):
- dup 函數(shù)用于復(fù)制鏈表節(jié)點所保存的值;
- free 函數(shù)用于釋放鏈表節(jié)點所保存的值;
- match 函數(shù)則用于對比鏈表節(jié)點所保存的值和另一個輸入值是否相等。
圖 3-2 是由一個 list 結(jié)構(gòu)和三個 listNode 結(jié)構(gòu)組成的鏈表。
3-2.png
特別注意:redid中l(wèi)ist結(jié)構(gòu)表頭指針 head、表尾指針 tail并不指向NULL的listNodeRedis 的鏈表實現(xiàn)的特性可以總結(jié)如下:雙端: 鏈表節(jié)點帶有 prev 和 next 指針, 獲取某個節(jié)點的前置節(jié)點和后置節(jié)點的復(fù)雜度都是 O(1) 。無環(huán): 表頭節(jié)點的 prev 指針和表尾節(jié)點的 next 指針都指向 NULL , 對鏈表的訪問以 NULL 為終點。帶表頭指針和表尾指針: 通過 list 結(jié)構(gòu)的 head 指針和 tail 指針, 程序獲取鏈表的表頭節(jié)點和表尾節(jié)點的復(fù)雜度為 O(1) 。帶鏈表長度計數(shù)器: 程序使用 list 結(jié)構(gòu)的 len屬性來對 list 持有的鏈表節(jié)點進(jìn)行計數(shù), 程序獲取鏈表中節(jié)點數(shù)量的復(fù)雜度為 O(1) 。多態(tài): 鏈表節(jié)點使用 void* 指針來保存節(jié)點值, 并且可以通過 list 結(jié)構(gòu)的 dup 、 free 、 match 三個屬性為節(jié)點值設(shè)置類型特定函數(shù), 所以鏈表可以用于保存各種不同類型的值。
因此,redis列表對象的適用場景也就是鏈表的適用場景:
1)數(shù)據(jù)量較小
2)不需要預(yù)先知道數(shù)據(jù)規(guī)模
3)適應(yīng)于頻繁的插入操作
缺點:
查找效率偏低,只能使用順序查找
重點回顧:
- 鏈表被廣泛用于實現(xiàn) Redis 的各種功能,比如列表鍵、發(fā)布與訂閱、慢查詢、監(jiān)視
器等。 - 每個鏈表節(jié)點由一個 listNode 結(jié)構(gòu)來表示,每個節(jié)點都有一個指向前置節(jié)點和后
置節(jié)點的指針,所以 Redis 的鏈表實現(xiàn)是雙端鏈表。 - 每個鏈表使用一個 list 結(jié)構(gòu)來表示,這個結(jié)構(gòu)帶有表頭節(jié)點指針、表尾節(jié)點指針,
以及鏈表長度等信息。 - 因為鏈表表頭節(jié)點的前置節(jié)點和表尾節(jié)點的后置節(jié)點都指向 NULL,所以 Redis 的鏈
表實現(xiàn)是無環(huán)鏈表。 - 通過為鏈表設(shè)置不同的類型特定函數(shù),Redis 的鏈表可以用于保存各種不同類型的值。
壓縮列表
當(dāng)一個列表鍵只包含少量列表項,并且每個列表項要么就是小整數(shù)值,要么就是長度比較短的字符串,那么Redis就會使用壓縮列表來做列表鍵的實現(xiàn)demo
redis>RPUSH lst 1 3 5 10086 "hello" "world" (interger) 6 redis>OBJECT ENCODING lst "ziplist"
2.當(dāng)一個哈希鍵只包含少量鍵值對的鍵和值要么就是小整數(shù)值,要么就是長度比較短的字符串,那么Redis就會使用壓縮列表來做哈希鍵的底層實現(xiàn)。
redis>HMSET profile "name" "Jack" "age" 28 "job" "Programmer" OK redis >OBJECT ENCODING profile "ziplist"
壓縮列表是 Redis 為了節(jié)約內(nèi)存而開發(fā)的, 由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型(sequential)數(shù)據(jù)結(jié)構(gòu)。
一個壓縮列表可以包含任意多個節(jié)點(entry), 每個節(jié)點可以保存一個字節(jié)數(shù)組或者一個整數(shù)值。
圖 7-1 展示了壓縮列表的各個組成部分, 表 7-1 則記錄了各個組成部分的類型、長度、以及用途。
7-1.png
表 7-1 壓縮列表各個組成部分的詳細(xì)說明
屬性 類型 長度 用途 zlbytes uint32_t 4 字節(jié) 記錄整個壓縮列表占用的內(nèi)存字節(jié)數(shù):在對壓縮列表進(jìn)行內(nèi)存重分配, 或者計算 zlend 的位置時使用。 zltail uint32_t 4 字節(jié) 記錄壓縮列表表尾節(jié)點距離壓縮列表的起始地址有多少字節(jié): 通過這個偏移量,程序無須遍歷整個壓縮列表就可以確定表尾節(jié)點的地址。 zllen uint16_t 2 字節(jié) 記錄了壓縮列表包含的節(jié)點數(shù)量: 當(dāng)這個屬性的值小于 UINT16_MAX (65535)時, 這個屬性的值就是壓縮列表包含節(jié)點的數(shù)量; 當(dāng)這個值等于 UINT16_MAX 時, 節(jié)點的真實數(shù)量需要遍歷整個壓縮列表才能計算得出。 entryX 列表節(jié)點 不定 壓縮列表包含的各個節(jié)點,節(jié)點的長度由節(jié)點保存的內(nèi)容決定。 zlend uint8_t 1 字節(jié) 特殊值 0xFF (十進(jìn)制 255 ),用于標(biāo)記壓縮列表的末端。 圖 7-2 展示了一個壓縮列表示例:
- 列表 zlbytes 屬性的值為 0x50 (十進(jìn)制 80), 表示壓縮列表的總長為 80 字節(jié)。
- 列表 zltail 屬性的值為 0x3c (十進(jìn)制 60), 這表示如果我們有一個指向壓縮列表起始地址的指針 p , 那么只要用指針 p 加上偏移量 60 , 就可以計算出表尾節(jié)點 entry3 的地址。
- 列表 zllen 屬性的值為 0x3 (十進(jìn)制 3), 表示壓縮列表包含三個節(jié)點。
7-2.png
圖 7-3 展示了另一個壓縮列表示例:
- 列表 zlbytes 屬性的值為 0xd2 (十進(jìn)制 210), 表示壓縮列表的總長為 210 字節(jié)。
- 列表 zltail 屬性的值為 0xb3 (十進(jìn)制 179), 這表示如果我們有一個指向壓縮列表起始地址的指針 p , 那么只要用指針 p 加上偏移量 179 , 就可以計算出表尾節(jié)點 entry5 的地址。
- 列表 zllen 屬性的值為 0x5 (十進(jìn)制 5), 表示壓縮列表包含五個節(jié)點。
7-3.png
entryX的構(gòu)成
每個壓縮列表節(jié)點都由 previous_entry_length 、 encoding 、 content 三個部分組成, 如圖 7-4 所示。
7-4.png
節(jié)點的 previous_entry_length 屬性以字節(jié)為單位, 記錄了壓縮列表中前一個節(jié)點的長度。
節(jié)點的 encoding 屬性記錄了節(jié)點的 content 屬性所保存數(shù)據(jù)的類型以及長度
節(jié)點的 content 屬性負(fù)責(zé)保存節(jié)點的值, 節(jié)點值可以是一個字節(jié)數(shù)組或者整數(shù), 值的類型和長度由節(jié)點的 encoding 屬性決定。
- 如果前一節(jié)點的長度小于 254 字節(jié), 那么 previous_entry_length 屬性的長度為 1 字節(jié): 前一節(jié)點的長度就保存在這一個字節(jié)里面。
- 如果前一節(jié)點的長度大于等于 254 字節(jié), 那么 previous_entry_length 屬性的長度為 5 字節(jié): 其中屬性的第一字節(jié)會被設(shè)置為 0xFE (十進(jìn)制值 254), 而之后的四個字節(jié)則用于保存前一節(jié)點的長度。
圖 7-8 展示了一個從表尾節(jié)點向表頭節(jié)點進(jìn)行遍歷的完整過程:
首先,我們擁有指向壓縮列表表尾節(jié)點 entry4 起始地址的指針 p1 (指向表尾節(jié)點的指針可以通過指向壓縮列表起始地址的指針加上 zltail 屬性的值得出);
通過用 p1 減去 entry4 節(jié)點 previous_entry_length 屬性的值, 我們得到一個指向 entry4 前一節(jié)點 entry3 起始地址的指針 p2 ;
通過用 p2 減去 entry3 節(jié)點 previous_entry_length 屬性的值, 我們得到一個指向 entry3 前一節(jié)點 entry2 起始地址的指針 p3 ;
通過用 p3 減去 entry2 節(jié)點 previous_entry_length 屬性的值, 我們得到一個指向 entry2 前一節(jié)點 entry1 起始地址的指針 p4 , entry1 為壓縮列表的表頭節(jié)點;
最終, 我們從表尾節(jié)點向表頭節(jié)點遍歷了整個列表。
7-8.png
列表命令的實現(xiàn)
因為列表鍵的值為列表對象, 所以用于列表鍵的所有命令都是針對列表對象來構(gòu)建的, 表 8-8 列出了其中一部分列表鍵命令, 以及這些命令在不同編碼的列表對象下的實現(xiàn)方法。
表 8-8 列表命令的實現(xiàn)
命令 ziplist 編碼的實現(xiàn)方法 linkedlist 編碼的實現(xiàn)方法 LPUSH 調(diào)用 ziplistPush 函數(shù), 將新元素推入到壓縮列表的表頭。 調(diào)用 listAddNodeHead 函數(shù), 將新元素推入到雙端鏈表的表頭。 RPUSH 調(diào)用 ziplistPush 函數(shù), 將新元素推入到壓縮列表的表尾。 調(diào)用 listAddNodeTail 函數(shù), 將新元素推入到雙端鏈表的表尾。 LPOP 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表的表頭節(jié)點, 在向用戶返回節(jié)點所保存的元素之后, 調(diào)用 ziplistDelete 函數(shù)刪除表頭節(jié)點。 調(diào)用 listFirst 函數(shù)定位雙端鏈表的表頭節(jié)點, 在向用戶返回節(jié)點所保存的元素之后, 調(diào)用 listDelNode 函數(shù)刪除表頭節(jié)點。 RPOP 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表的表尾節(jié)點, 在向用戶返回節(jié)點所保存的元素之后, 調(diào)用 ziplistDelete 函數(shù)刪除表尾節(jié)點。 調(diào)用 listLast 函數(shù)定位雙端鏈表的表尾節(jié)點, 在向用戶返回節(jié)點所保存的元素之后, 調(diào)用 listDelNode 函數(shù)刪除表尾節(jié)點。 LINDEX 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表中的指定節(jié)點, 然后返回節(jié)點所保存的元素。 調(diào)用 listIndex 函數(shù)定位雙端鏈表中的指定節(jié)點, 然后返回節(jié)點所保存的元素。 LLEN 調(diào)用 ziplistLen 函數(shù)返回壓縮列表的長度。 調(diào)用 listLength 函數(shù)返回雙端鏈表的長度。 LINSERT 插入新節(jié)點到壓縮列表的表頭或者表尾時, 使用 ziplistPush 函數(shù); 插入新節(jié)點到壓縮列表的其他位置時, 使用 ziplistInsert 函數(shù)。 調(diào)用 listInsertNode 函數(shù), 將新節(jié)點插入到雙端鏈表的指定位置。 LREM 遍歷壓縮列表節(jié)點, 并調(diào)用 ziplistDelete 函數(shù)刪除包含了給定元素的節(jié)點。 遍歷雙端鏈表節(jié)點, 并調(diào)用 listDelNode 函數(shù)刪除包含了給定元素的節(jié)點。 LTRIM 調(diào)用 ziplistDeleteRange 函數(shù), 刪除壓縮列表中所有不在指定索引范圍內(nèi)的節(jié)點。 遍歷雙端鏈表節(jié)點, 并調(diào)用 listDelNode 函數(shù)刪除鏈表中所有不在指定索引范圍內(nèi)的節(jié)點。 LSET 調(diào)用 ziplistDelete 函數(shù), 先刪除壓縮列表指定索引上的現(xiàn)有節(jié)點, 然后調(diào)用 ziplistInsert 函數(shù), 將一個包含給定元素的新節(jié)點插入到相同索引上面。 調(diào)用 listIndex 函數(shù), 定位到雙端鏈表指定索引上的節(jié)點, 然后通過賦值操作更新節(jié)點的值。
總結(jié)
以上是生活随笔為你收集整理的jtoken判断是否包含键_Redis列表键(linkedlist/ziplist)的介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: aspen怎么做灵敏度分析_数据分析终极
- 下一篇: spring mysql json_Sp