本篇文章給大家?guī)淼膬热菔顷P于redis壓縮列表的詳細介紹(示例講解),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
此篇文章是主要介紹Redis在數(shù)據(jù)存儲方面的其中一種方式,壓縮列表。
本文會介紹
1、壓縮列表(ziplist)的使用場景?
2.如何達到節(jié)約內存的效果?
3.壓縮列表的存儲格式?
4. 連鎖更新的問題??
5. conf文件配置。
在實踐上的操作主要是對conf配置文件進行配置,具體上沒有確切的一個值,更多是經(jīng)驗值。也有的項目會直接使用原本的默認值。此篇對于更好地理解一個數(shù)據(jù)庫底層的存儲邏輯會有一點幫助。修學儲能,既要博,也要淵。希望這篇文章對同樣也是在學習Redis的各位同伴有點用。(推薦教程:Redis教程)
一、壓縮列表(ziplist)的使用場景:
Redis為了優(yōu)化數(shù)據(jù)存儲,節(jié)約內存,在列表、字典(哈希鍵)和有序集合的底層實現(xiàn)了使用壓縮列表這一優(yōu)化方案。
例如,假如一個哈希鍵里面存儲的字符串比較短,那么Redis就會將它用壓縮列表的格式去存儲,即轉換為字節(jié)數(shù)組存儲。而一個哈希鍵內部存儲的整數(shù)值比較小,同樣也會把它存儲為壓縮列表的一個節(jié)點。同理,列表鍵的對小數(shù)據(jù)的存儲跟哈希鍵的操作類似。
如此說來,壓縮列表并不是開發(fā)者可以直接調用的Redis中的一種存儲數(shù)據(jù)結構,而是Redis中為優(yōu)化數(shù)據(jù)存儲而在底層做的一項努力。理解好這點還是比較重要的。
二、如何達到節(jié)約內存的效果?
壓縮列表是一種序列化的數(shù)據(jù)結構,這種數(shù)據(jù)結構的功能是將一系列數(shù)據(jù)與其編碼信息存儲在一塊連續(xù)的內存區(qū)域,這塊內存物理上是連續(xù)的。但邏輯上被分為多個組成部分,即節(jié)點。目的是為了在一定可控的時間復雜度條件下盡可能的減少不必要的內存開銷,從而達到節(jié)省內存的效果。需要理解是怎么達到節(jié)約內存作用的,還需要去了解壓縮列表的存儲格式。
三、壓縮列表的存儲格式:
壓縮列表(ziplist)是Redis列表鍵、哈希鍵和有序集合鍵的底層實現(xiàn)之一,其實質是一種序列化的數(shù)據(jù)存儲結構。有別于普通情況下,Redis用雙端鏈表表示列表,使用散列表表示哈希鍵,用散列表+跳躍表表示有序集合。當一個列表或者哈希字典/有序集合只包含很少內容,并且每一個列表項或者哈希項/有序集合項如果是小整數(shù),或者比較短的字符串。那么Redis就會用壓縮列表來做底層的實現(xiàn)。
壓縮列表由一系列經(jīng)Redis特殊編碼的連續(xù)內存塊組成,每一個內存塊稱為一個節(jié)點(entry),而一個壓縮列表可以包含很多個節(jié)點。每個節(jié)點存儲的數(shù)據(jù)格式可以是字節(jié)數(shù)組(中文字符串等都會轉換為字節(jié)數(shù)組)或者整數(shù)值。
字節(jié)數(shù)組的長度可以是以下的其中一種:
1. 長度小于等于63字節(jié)(2的6次方)
2. 長度小于等于16383字節(jié)(2的14次方)
3. 長度小于等于4294967295字節(jié)(2的32次方)
整數(shù)值可能是以下六種中的其中一種:
1. 4位長,介于0-12之間的無符號整數(shù)
2. 1字節(jié)長的有符號整數(shù)
3. 3字節(jié)長的有符號整數(shù)
4. int16_t類型整數(shù)
5. int32_類型整數(shù)
6. int64_t類型整數(shù)
普通存儲格式下和壓縮列表存儲格式下的不同點:
列表存儲結構典型的為雙端鏈表,每一個值都是用一個節(jié)點來表示,每個節(jié)點都會有指向前一個節(jié)點和后一個節(jié)點的指針,以及指向節(jié)點包含的字符串值的指針。而字符串值又分為3個部分存儲,第一部分存儲字符串長度,第二部分存儲字符串值中剩余可用的字節(jié)量,第三部分存儲的則是字符串數(shù)據(jù)本身。所以一個節(jié)點往往都需要存儲3個指針、2個記錄字符串信息的整數(shù)、字符串本省和一個額外的字節(jié)。總體上額外的開銷是很大的(21字節(jié))。
壓縮列表節(jié)點的格式:
每一個節(jié)點都有previous_entry_length,encoding,content三個部分組成,在遍歷壓縮列表的時候是從后往前遍歷的。
1. previous_entry_length記錄了前一個節(jié)點的長度,只要用當前指針減去這個值就可以達到前一個節(jié)點的起始地址。
2. encoding記錄了節(jié)點content屬性所保存數(shù)據(jù)的類型和長度
3. content記錄了一個節(jié)點的值
顯然壓縮列表這種方式節(jié)約了不少存儲空間。但同時也會引發(fā)下面的問題。
四、連鎖更新的問題:
一般而言如果前一個節(jié)點的整體長度小于254字節(jié),previous_entry_length屬性只需要1個字節(jié)的空間來保存這個長度值。而當前一個節(jié)點大于254字節(jié)的時候,previous_entry_length屬性要用5個字節(jié)長的空間來記錄長度值。
當長度為254字節(jié)左右的節(jié)點前插入一個新的節(jié)點的時候,需要增加previous_entry_length來記錄這個節(jié)點到新節(jié)點的偏移量。這個時候,這個節(jié)點的長度肯定就大于254字節(jié)了。所以這個節(jié)點的后一個節(jié)點就不能只用一個字節(jié)的previous_entry_length來記錄這個節(jié)點的信息了,而是需要5個字節(jié)來記錄。如果連續(xù)多個節(jié)點的長度都為254字節(jié)左右,在其中的某一個節(jié)點前/后發(fā)生節(jié)點的插入和刪除(刪除的推理與插入相反,原本用5字節(jié)記錄前一節(jié)點的可能變?yōu)?字節(jié)),都可能引發(fā)連鎖的更新,顯然,這樣對系統(tǒng)地運行效率是很不利的。不過,在實際應用中這種情況還是比較少發(fā)生的。
而雙端鏈表在節(jié)點的更新、增加和刪除上顯得就會“輕松”很多了。 因為每一個節(jié)點存儲的信息都是相對獨立的。??
實踐意義:
要預估一個節(jié)點大概占據(jù)多少字節(jié)的存儲空間,適當?shù)卣{整字段的存儲格式而不要使存儲的字段值占據(jù)存儲空間落在254字節(jié)(除去encoding屬性和previous_entry_length屬性)左右。
Redis中查看字符串和哈希鍵值的長度相關命令:
1. 查詢字符串鍵對應的值長度
命令:
例如:
127.0.0.1:6379> strlen m_name
(Integer) 8
2. 查詢哈希鍵某一個域長度
命令:
Hstrlen
例如:
127.0.0.1:6379> hstrlen good_list good_list1
(integer) 226
五、Conf文件配置:
通過修改配置文件,可以控制是否使用壓縮列表存儲相關鍵的最大元素個數(shù)和最大元素的大小
Conf文件中的配置:
1.
[] -max-ziplist-entries : 表示對于鍵的最大元素個數(shù),即一個鍵中在該指定值下的數(shù)量的節(jié)點個數(shù)都會用壓縮列表來儲存
[] -max-ziplist-value :表示壓縮列表中每個節(jié)點的最大體積是多少字節(jié)
實際使用中,一個列表鍵/哈希鍵的某一個元素往往存儲著比較大的信息量,會大于64字節(jié),所以配置時很有可能會比64大,同時考慮到實際存儲數(shù)據(jù)的容量大小以及上面談到的previous_entry_length的大小問題,對[] -max-ziplist-value進行合理的配置。
配置文件內容:
##############?ADVANCED?CONFIG?########################## 哈希鍵 #?Hashes?are?encoded?using?a?memory?efficient?data?structure?when?they?have?a #?small?number?of?entries,?and?the?biggest?entry?does?not?exceed?a?given #?threshold.?These?thresholds?can?be?configured?using?the?following?directives. hash-max-ziplist-entries?512 hash-max-ziplist-value?64 有序集合鍵 #?Similarly?to?hashes?and?lists,?sorted?sets?are?also?specially?encoded?in #?order?to?save?a?lot?of?space.?This?encoding?is?only?used?when?the?length?and #?elements?of?a?sorted?set?are?below?the?following?limits: zset-max-ziplist-entries?128 zset-max-ziplist-value?64 列表鍵,比較特殊,直接使用制定大小kb字節(jié)數(shù)表示(有些conf文件的列表鍵與hash鍵的表達式?jīng)]太大區(qū)別) #?Lists?are?also?encoded?in?a?special?way?to?save?a?lot?of?space. #?The?number?of?entries?allowed?per?internal?list?node?can?be?specified #?as?a?fixed?maximum?size?or?a?maximum?number?of?elements. #?For?a?fixed?maximum?size,?use?-5?through?-1,?meaning: #?-5:?max?size:?64?Kb??<p>案例:</p><p>修改配置前使用默認配置:</p><p>hash-max-ziplist-entries 512</p><p>hash-max-ziplist-value 64</p><p>127.0.0.1:6379> hstrlen good_list good_list1</p><p>(integer) 226</p><p>127.0.0.1:6379> Object encoding good_list</p><p>"hashtable"</p><p>修改配置:</p><p>hash-max-ziplist-entries 512</p><p><strong>hash-max-ziplist-value 254</strong></p><p>注意:修改配置后需要重啟服務器</p><p>127.0.0.1:6379> hstrlen good_list good_list1</p><p>(integer) 226</p><p>127.0.0.1:6379> object encoding good_list</p><p><strong>"ziplist"</strong></p><p>可以看到存儲方式已將變?yōu)閦iplist</p><p><strong>較官方的壓力測試和指導建議:</strong></p><p>當一個壓縮列表的元素數(shù)量上升到幾千(實際使用可能遠小于這個值)的時候,壓縮列表的性能可能會下降,因為Redis在操作這種結構的時候,編解碼會出現(xiàn)一定的壓力。</p><p>壓縮列表的長度限制在500-2000之內,每個元素體積限制在128字節(jié)或以下,壓縮列表的的性能都會處于合理范圍之內。</p><p> </p>