图解Redis之数据结构篇——压缩列表
前言
????同整數(shù)集合一樣壓縮列表也不是基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),而是 Redis 自己設(shè)計的一種數(shù)據(jù)存儲結(jié)構(gòu)。它有點兒類似數(shù)組,通過一片連續(xù)的內(nèi)存空間,來存儲數(shù)據(jù)。不過,它跟數(shù)組不同的一點是,它允許存儲的數(shù)據(jù)大小不同。
一、壓縮列表
????聽到“壓縮”兩個字,直觀的反應(yīng)就是節(jié)省內(nèi)存。之所以說這種存儲結(jié)構(gòu)節(jié)省內(nèi)存,是相較于數(shù)組的存儲思路而言的。我們知道,數(shù)組要求每個元素的大小相同,如果我們要存儲不同長度的字符串,那我們就需要用最大長度的字符串大小作為元素的大小(假設(shè)是20個字節(jié))。存儲小于 20 個字節(jié)長度的字符串的時候,便會浪費部分存儲空間。
????數(shù)組的優(yōu)勢占用一片連續(xù)的空間可以很好的利用CPU緩存訪問數(shù)據(jù)。如果我們想要保留這種優(yōu)勢,又想節(jié)省存儲空間我們可以對數(shù)組進(jìn)行壓縮。
?????但是這樣有一個問題,我們在遍歷它的時候由于不知道每個元素的大小是多少,因此也就無法計算出下一個節(jié)點的具體位置。這個時候我們可以給每個節(jié)點增加一個lenght的屬性。
????如此。我們在遍歷節(jié)點的之后就知道每個節(jié)點的長度(占用內(nèi)存的大小),就可以很容易計算出下一個節(jié)點再內(nèi)存中的位置。這種結(jié)構(gòu)就像一個簡單的壓縮列表了。
二、Redis壓縮列表
????壓縮列表(zip1ist)是列表和哈希的底層實現(xiàn)之一。
????當(dāng)一個列表只包含少量列表項,并且每個列表項要么就是小整數(shù)值,要么就是長度比較短的字符串,那么Redis就會使用壓縮列表來做列表的底層實現(xiàn)。
????當(dāng)一個哈希只包含少量鍵值對,比且每個鍵值對的鍵和值要么就是小整數(shù)值,要么就是長度比較短的字符串,那么Redis就會使用壓縮列表來做哈希的底層實現(xiàn)。
2.1 Redis壓縮列表的構(gòu)成
????壓縮列表是Redis為了節(jié)約內(nèi)存而開發(fā)的,是由一系列特殊編碼的連續(xù)內(nèi)存塊組成的順序型(sequential)數(shù)據(jù)結(jié)枃。一個壓縮列表可以包含任意多個節(jié)點(entry),每個節(jié)點可以保存一個字節(jié)數(shù)組或者一個整數(shù)值,如下圖。
示例:
????如上圖,展示了一個總長為80字節(jié),包含3個節(jié)點的壓縮列表。如果我們有一個指向壓縮列表起始地址的指針p,那么表為節(jié)點的地址就是P+60。
2.2 Redis壓縮列表節(jié)點的構(gòu)成
????每個壓縮列表節(jié)點可以保存一個字節(jié)數(shù)組或者一個整數(shù)值。其中,字節(jié)數(shù)組可以是以下三種長度中的一種。
- 長度小于等于63(2^6-1)字節(jié)的字節(jié)數(shù)組;
- 長度小于等于16383(2^14-1)字節(jié)的字節(jié)數(shù)組
- 長度小于等于4294967295(2^32-1)字節(jié)的字節(jié)數(shù)組
整數(shù)值可以是以下6種長度中的一種
- 4位長,介于0至12之間的無符號整數(shù)
- 1字節(jié)長的有符號整數(shù)
- 3字節(jié)長的有符號整數(shù)
- int16_t類型整數(shù)
- int32_t類型整數(shù)
- int64_t類型整數(shù)
????節(jié)點的 previous_entry_length屬性以字節(jié)為單位,記錄了壓縮列表中前一個節(jié)點的長度。 previous_entry_length屬性的長度可以是1字節(jié)或者5字節(jié)。
- 如果前一節(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é)點的長度.
????節(jié)點的encoding屬性記錄了節(jié)點的content屬性所保存數(shù)據(jù)的類型以及長度。
- 一字節(jié)、兩字節(jié)或者五字節(jié)長,值的最高位為00、01或者10的是字節(jié)數(shù)組編碼這種編碼表示節(jié)點的 content屬性保存著字節(jié)數(shù)組,數(shù)組的長度由編碼除去最高兩位之后的其他位記錄。
- 一字節(jié)長,值的最高位以11開頭的是整數(shù)編碼:這種編碼表示節(jié)點的content屬性保存著整數(shù)值,整數(shù)值的類型和長度由編碼除去最高兩位之后的其他位記錄。
????節(jié)點的content屬性負(fù)責(zé)保存節(jié)點的值,節(jié)點值可以是一個字節(jié)數(shù)組或者整數(shù),值的類型和長度由節(jié)點的encoding屬性決定。
- 編碼的最高兩位00表示節(jié)點保存的是一個字節(jié)數(shù)組。
- 編碼的后六位001011記錄了字節(jié)數(shù)組的長度11。
- content屬性保存著節(jié)點的值"hello world"。
- 編碼11000000表示節(jié)點保存的是一個int16_t類型的整數(shù)值;
- content屬性保存著節(jié)點的值10086
2.3 常用操作的時間復(fù)雜度
| 創(chuàng)建一個新的壓縮列表 | O(1) |
| 創(chuàng)建一個包含給定值的新節(jié)點,并將這個新節(jié)點添加到壓縮列表的表頭或者表尾 | 平均O(N),最壞O(N^2)(可能發(fā)生連鎖更新) |
| 將包含給定值的新節(jié)點插人到給定節(jié)點之后 | 平均O(N),最壞O(N^2)(可能發(fā)生連鎖更新) |
| 返回壓縮列表給定索引上的節(jié)點 | O(N) |
| 在壓縮列表中査找并返回包含了給定值的節(jié)點 | 因為節(jié)點的值可能是一個字節(jié)數(shù)組,所以檢查節(jié)點值和給定值是否相同的復(fù)雜度為O(N),而查找整個列表的復(fù)雜度則為(N^2) |
| 返回給定節(jié)點的下一個節(jié)點 | O(1) |
| 返回給定節(jié)點的前一個節(jié)點 | O(1) |
| 獲取給定節(jié)點所保存的值 | O(1) |
| 從壓縮列表中刪除給定的節(jié)點 | 平均O(N),最壞O(N^2)(可能發(fā)生連鎖更新) |
| 刪除壓縮列表在給定索引上的連續(xù)多個 | 平均O(N),最壞O(N^2)(可能發(fā)生連鎖更新) |
| 返回壓縮列表目前占用的內(nèi)存字節(jié)數(shù) | O(1) |
| 返回壓縮列表目前包含的節(jié)點數(shù)量 | 點數(shù)量小于65535時為O(1),大于65535時為O(N) |
本文重點
壓縮列表是Redis為節(jié)約內(nèi)存自己設(shè)計的一種順序型數(shù)據(jù)結(jié)構(gòu)。
- 壓縮列表被用作列表鍵和哈希鍵的底層實現(xiàn)之一。
- 壓縮列表可以包含多個節(jié)點,每個節(jié)點可以保存一個字節(jié)數(shù)組或者整數(shù)值。
添加新節(jié)點到壓縮列表,或者從壓縮列表中刪除節(jié)點,可能會引發(fā)連鎖更新操作,但這種操作出現(xiàn)的幾率并不高。
參考
《Redis設(shè)計與實現(xiàn)》
《Redis開發(fā)與運維》
《Redis官方文檔》
-----END-----
轉(zhuǎn)載于:https://www.cnblogs.com/hunternet/p/11306690.html
總結(jié)
以上是生活随笔為你收集整理的图解Redis之数据结构篇——压缩列表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在100克水中放入10克盐,盐的重量占盐
- 下一篇: 银行快捷金额什么意思