前言
- 在之前我們已經學習了redis五大數據結構中的list結構。其內部是linkedList和zipList兩種結構。這是我們已經學習的內容。之前我沒有結合操作具體查看。事實上在兩者中還存在一種結合體quickList
結構演變
- 在上面我們添加了一個key為zlist的數據。通過object encoding zlist查看底層就是通過quicklist來構建的。之前在ziplist章節匯總我們了解到在redis中hash和list基本數據結構都使用了ziplist存儲數據的。在list中我們確實quicklist。這里我們提前說明下quicklist內部就是基於ziplist來實現的。
linkedList
- 在開場quicklist之前我們簡單梳理下之前學過的linkedList ,他是一種常見的雙線鏈表。通過兩個指針完成我們鏈表的構建。
C++指針
- redis是基於內存運行的,而內存有十分的寶貴所以redis在設計了雙線鏈表后覺得有點耗內存。因為指針本身也是需要開辟空間的。根據系統的不同指針占位不同。這里我總結了一下一個指針占位就是一個系統操作的基本位
- 這里基本位是什么意思呢?加入你是64位系統那么一個指針就是64位即8個字節。如果你是32位系統那么一個指針就是32位即4個字節
- 也就是說如果我在redis中向雙向鏈表中存儲N個英文字母,我們又知道一個應為字母占1個字節。那么這N個元素就是N的listNode . 那么維持着N個listNode中間就需要2*(N-1)個指針。在64位系統中也就是我們需要開辟將近129倍的空間來存儲內容。上述情況我們只有N個字節的內容,卻需要
2*(N-1)*8+N
個字節來構建listNode。 - 隨着節點的遞增我們浪費程度越離譜。所以redis在雙向鏈表的基礎上結合了ziplist進行改良。
過渡原因
ziplist
- 在ziplist章節中我們知道ziplist是一塊連續內存,是redis對內存的一種改良結構。ziplist實現了內存的高使用率!
linkedlist+ziplist好處
quicklist引入
- quicklist是在redis3.2之后引入的,筆者這里使用的是redis6.4方便源碼好像並沒有quicklist源碼。
- 后來翻閱了之后redis6.4好像取消了quicklist . 結構。所以我又特別下了一個3.2的版本。這里具體的是redis3.2.4版本!!!
廬山真面目
quicklist
- 通過他的源碼我們很清晰的看出他的內部數據結構!這個大家應該很熟悉了。quicklist可以說就是我們之前的linkedList 結構。內部就是雙向鏈表只不過里面的屬性稍微多了點
- 通過圖示是不是感覺和linkedList一樣。
- 接下來我們看看quicklist中各個屬性的含義吧
quicklistNode
- quicklist只是一個抽象的概念,真正負責數據的存儲的是組成quicklist的成員quicklistNode 。
- 各個屬性的作用
-
通過上面的屬性介紹,我們也可以了解了解到node節點中的數據結構就是ziplist 。在ziplist基礎上會在進行壓縮達到內存更高的使用效率!
-
關於壓縮這里我們不用太去了解!主要目的就是一種編碼,這種編碼是無法真正使用的在使用期間redis會進行解碼操作。在解碼操作期間就是通過recompress屬性來標記的。
insert
- 在了解quicklist基本結構之后我們在看看insert時結構會發生哪些變化!上面我們也提到了在redis.conf配置文件中
list-max-ziplist-size
屬性是用來設置quicklist中每個節點中的ziplist存儲的大小設置的。
屬性值 | 作用 |
---|---|
-1 | 每個quicklistNode節點的ziplist所占字節數不能超過4kb |
-2 | 每個quicklistNode節點的ziplist所占字節數不能超過8kb |
-3 | 每個quicklistNode節點的ziplist所占字節數不能超過16kb |
-4 | 每個quicklistNode節點的ziplist所占字節數不能超過32kb |
-5 | 每個quicklistNode節點的ziplist所占字節數不能超過64kb |
int | ziplist包含的entry上限 |
兩端插入
- 第一種情況就是我們需要插入的數據是在兩端的。如上圖所示我們在redis.conf配置文件中設置的
list-max-ziplist-size: 2
。表示內部節點ziplist中entry個數最大為2 。此時我們head頭部節點中已經存儲了兩個內容,tail尾部節點存儲的是1個節點! - 這個時候如果我們想頭部添加一個元素是obj1 。 可想而知我們是無法加入的,這個時候redis會重新創建一個ziplist結構並包含obj1 ,將新創建的ziplist加入到鏈表的頭部之后
- 而obj2加入尾結點時,因為尾結點的節點數是1還未達到峰值2,所以直接就加入了。最終的效果圖如下
中間插入
st=>start: Insert
ziplistInsert=>operation: 向ziplist中插入
subziplistInsert=>operation: 將該ziplist拆分兩個ziplist, 在對應位置加入
insertNear=>operation: 插入相鄰的ziplist中
newZipInsert=>operation: 新建ziplist插入
cond=>condition: ziplist是否可以容納
headtailCond=>condition: 插入位置在ziplist兩端
nearheadtailCond=>condition: 相鄰ziplist是否可以容納
e=>end: 快樂的一天
st->cond
cond(yes)->ziplistInsert
cond(no)->headtailCond(yes)->nearheadtailCond
headtailCond(no)->subziplistInsert
nearheadtailCond(yes)->insertNear
nearheadtailCond(no)->newZipInsert