【redis前傳】集思廣益之quicklist,取其精華去其糟粕

前言

  • 在之前我們已經學習了redis五大數據結構中的list結構。其內部是linkedList和zipList兩種結構。這是我們已經學習的內容。之前我沒有結合操作具體查看。事實上在兩者中還存在一種結合體quickList

image-20210715191310177

結構演變

  • 在上面我們添加了一個key爲zlist的數據。通過object encoding zlist查看底層就是通過quicklist來構建的。之前在ziplist章節彙總我們瞭解到在redis中hash和list基本數據結構都使用了ziplist存儲數據的。在list中我們確實quicklist。這裏我們提前說明下quicklist內部就是基於ziplist來實現的。

linkedList

  • 在開場quicklist之前我們簡單梳理下之前學過的linkedList ,他是一種常見的雙線鏈表。通過兩個指針完成我們鏈表的構建。

image-20210723133046693

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進行改良。

過渡原因

image-20210723133853290

ziplist

  • 在ziplist章節中我們知道ziplist是一塊連續內存,是redis對內存的一種改良結構。ziplist實現了內存的高使用率!

image-20210723134013816

linkedlist+ziplist好處

image-20210723134105776

quicklist引入

  • quicklist是在redis3.2之後引入的,筆者這裏使用的是redis6.4方便源碼好像並沒有quicklist源碼。
  • 後來翻閱了之後redis6.4好像取消了quicklist . 結構。所以我又特別下了一個3.2的版本。這裏具體的是redis3.2.4版本!!!

廬山真面目

quicklist

image-20210723134438938

  • 通過他的源碼我們很清晰的看出他的內部數據結構!這個大家應該很熟悉了。quicklist可以說就是我們之前的linkedList 結構。內部就是雙向鏈表只不過裏面的屬性稍微多了點

image-20210723134617683

  • 通過圖示是不是感覺和linkedList一樣。

image-20210723134817146

  • 接下來我們看看quicklist中各個屬性的含義吧

image-20210723134859582

quicklistNode

  • quicklist只是一個抽象的概念,真正負責數據的存儲的是組成quicklist的成員quicklistNode 。

image-20210723135204092

  • 各個屬性的作用

image-20210723135327554

  • 通過上面的屬性介紹,我們也可以瞭解瞭解到node節點中的數據結構就是ziplist 。在ziplist基礎上會在進行壓縮達到內存更高的使用效率!

  • 關於壓縮這裏我們不用太去了解!主要目的就是一種編碼,這種編碼是無法真正使用的在使用期間redis會進行解碼操作。在解碼操作期間就是通過recompress屬性來標記的。

image-20210723135734482

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上限

兩端插入

image-20210723140744723

  • 第一種情況就是我們需要插入的數據是在兩端的。如上圖所示我們在redis.conf配置文件中設置的list-max-ziplist-size: 2 。表示內部節點ziplist中entry個數最大爲2 。此時我們head頭部節點中已經存儲了兩個內容,tail尾部節點存儲的是1個節點!
  • 這個時候如果我們想頭部添加一個元素是obj1 。 可想而知我們是無法加入的,這個時候redis會重新創建一個ziplist結構幷包含obj1 ,將新創建的ziplist加入到鏈表的頭部之後

image-20210723141323765

  • 而obj2加入尾結點時,因爲尾結點的節點數是1還未達到峯值2,所以直接就加入了。最終的效果圖如下

image-20210723141540152

中間插入

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

總結

image-20210723144626592

參考文獻

lzf壓縮算法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章