Redis壓縮列表

此篇文章是主要介紹Redis在數據存儲方面的其中一種方式,壓縮列表。本文會介紹1. 壓縮列表(ziplist)的使用場景 2.如何達到節約內存的效果?3.壓縮列表的存儲格式 4. 連鎖更新的問題  5. conf文件配置。在實踐上的操做主要是對conf配置文件進行配置,具體上沒有確切的一個值,更可能是經驗值。也有的項目會直接使用本來的默認值。此篇對於更好地理解一個數據庫底層的存儲邏輯會有一點幫助。修學儲能,既要博,也要淵。但願這篇文章對一樣也是在學習Redis的各位同伴有點用。node

 

1、壓縮列表(ziplist)的使用場景:redis

Redis爲了優化數據存儲,節約內存,在列表、字典(哈希鍵)和有序集合的底層實現了使用壓縮列表這一優化方案。數據庫

例如,假如一個哈希鍵裏面存儲的字符串比較短,那麼Redis就會將它用壓縮列表的格式去存儲,即轉換爲字節數組存儲。而一個哈希鍵內部存儲的整數值比較小,一樣也會把它存儲爲壓縮列表的一個節點。同理,列表鍵的對小數據的存儲跟哈希鍵的操做相似。數組

 

如此說來,壓縮列表並非開發者能夠直接調用的Redis中的一種存儲數據結構,而是Redis中爲優化數據存儲而在底層作的一項努力。理解好這點仍是比較重要的。服務器

 

2、如何達到節約內存的效果?數據結構

壓縮列表是一種序列化的數據結構,這種數據結構的功能是將一系列數據與其編碼信息存儲在一塊連續的內存區域,這塊內存物理上是連續的。但邏輯上被分爲多個組成部分,即節點。目的是爲了在必定可控的時間複雜度條件下儘量的減小沒必要要的內存開銷,從而達到節省內存的效果。須要理解是怎麼達到節約內存做用的,還須要去了解壓縮列表的存儲格式。性能

 

3、壓縮列表的存儲格式:學習

壓縮列表(ziplist)是Redis列表鍵、哈希鍵和有序集合鍵的底層實現之一,其實質是一種序列化的數據存儲結構。有別於普通狀況下,Redis用雙端鏈表表示列表,使用散列表表示哈希鍵,用散列表+跳躍表表示有序集合。當一個列表或者哈希字典/有序集合只包含不多內容,而且每個列表項或者哈希項/有序集合項若是是小整數,或者比較短的字符串。那麼Redis就會用壓縮列表來作底層的實現。測試

 

壓縮列表由一系列經Redis特殊編碼的連續內存塊組成,每個內存塊稱爲一個節點(entry),而一個壓縮列表能夠包含不少個節點。每一個節點存儲的數據格式能夠是字節數組(中文字符串等都會轉換爲字節數組)或者整數值。優化

字節數組的長度能夠是如下的其中一種:

1. 長度小於等於63字節(2的6次方)

2. 長度小於等於16383字節(2的14次方)

3. 長度小於等於4294967295字節(2的32次方)

整數值多是如下六種中的其中一種:

1. 4位長,介於0-12之間的無符號整數

2. 1字節長的有符號整數

3. 3字節長的有符號整數

4. int16_t類型整數

5. int32_類型整數

6. int64_t類型整數

 

普通存儲格式下和壓縮列表存儲格式下的不一樣點:

列表存儲結構典型的爲雙端鏈表,每個值都是用一個節點來表示,每一個節點都會有指向前一個節點和後一個節點的指針,以及指向節點包含的字符串值的指針。而字符串值又分爲3個部分存儲,第一部分存儲字符串長度,第二部分存儲字符串值中剩餘可用的字節量,第三部分存儲的則是字符串數據自己。因此一個節點每每都須要存儲3個指針、2個記錄字符串信息的整數、字符串本省和一個額外的字節。整體上額外的開銷是很大的(21字節)。

 

壓縮列表節點的格式:

每個節點都有previous_entry_length,encoding,content三個部分組成,在遍歷壓縮列表的時候是從後往前遍歷的。

1. previous_entry_length記錄了前一個節點的長度,只要用當前指針減去這個值就能夠達到前一個節點的起始地址。

2. encoding記錄了節點content屬性所保存數據的類型和長度

3. content記錄了一個節點的值

 

顯然壓縮列表這種方式節約了很多存儲空間。但同時也會引起下面的問題。

 

4、連鎖更新的問題:

通常而言若是前一個節點的總體長度小於254字節,previous_entry_length屬性只須要1個字節的空間來保存這個長度值。而當前一個節點大於254字節的時候,previous_entry_length屬性要用5個字節長的空間來記錄長度值。

當長度爲254字節左右的節點前插入一個新的節點的時候,須要增長previous_entry_length來記錄這個節點到新節點的偏移量。這個時候,這個節點的長度確定就大於254字節了。因此這個節點的後一個節點就不能只用一個字節的previous_entry_length來記錄這個節點的信息了,而是須要5個字節來記錄。若是連續多個節點的長度都爲254字節左右,在其中的某一個節點前/後發生節點的插入和刪除(刪除的推理與插入相反,本來用5字節記錄前一節點的可能變爲1字節),均可能引起連鎖的更新,顯然,這樣對系統地運行效率是很不利的。不過,在實際應用中這種狀況仍是比較少發生的。

    而雙端鏈表在節點的更新、增長和刪除上顯得就會「輕鬆」不少了。 由於每個節點存儲的信息都是相對獨立的。  

 

實踐意義:

要預估一個節點大概佔據多少字節的存儲空間,適當地調整字段的存儲格式而不要使存儲的字段值佔據存儲空間落在254字節(除去encoding屬性和previous_entry_length屬性)左右。

 

Redis中查看字符串和哈希鍵值的長度相關命令:

1. 查詢字符串鍵對應的值長度

命令:

Strlen

例如:

127.0.0.1:6379> strlen m_name

(integer) 8

 

2. 查詢哈希鍵某一個域長度

命令:

Hstrlen

例如:

127.0.0.1:6379> hstrlen good_list good_list1

(integer) 226

 

 

5、Conf文件配置:

經過修改配置文件,能夠控制是否使用壓縮列表存儲相關鍵的最大元素個數和最大元素的大小

Conf文件中的配置:
1.

[] -max-ziplist-entries : 表示對於鍵的最大元素個數,即一個鍵中在該指定值下的數量的節點個數都會用壓縮列表來儲存

[] -max-ziplist-value :表示壓縮列表中每一個節點的最大致積是多少字節

實際使用中,一個列表鍵/哈希鍵的某一個元素每每存儲着比較大的信息量,會大於64字節,因此配置時頗有可能會比64大,同時考慮到實際存儲數據的容量大小以及上面談到的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字節數表示(有些conf文件的列表鍵與hash鍵的表達式沒太大區別)
# 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  <-- not recommended for normal workloads
# -4: max size: 32 Kb  <-- not recommended
# -3: max size: 16 Kb  <-- probably not recommended
# -2: max size: 8 Kb   <-- good
# -1: max size: 4 Kb   <-- good
# Positive numbers mean store up to _exactly_ that number of elements
# per list node.
# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
# but if your use case is unique, adjust the settings as necessary.
list-max-ziplist-size -2

 

案例:

修改配置前使用默認配置:

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

 

127.0.0.1:6379> hstrlen good_list good_list1

(integer) 226

127.0.0.1:6379> object encoding good_list

"hashtable"

 

修改配置:

hash-max-ziplist-entries 512

hash-max-ziplist-value 254

注意:修改配置後須要重啓服務器

127.0.0.1:6379> hstrlen good_list good_list1

(integer) 226

127.0.0.1:6379> object encoding good_list

"ziplist"

 

能夠看到存儲方式已將變爲ziplist

 

較官方的壓力測試和指導建議:

當一個壓縮列表的元素數量上升到幾千(實際使用可能遠小於這個值)的時候,壓縮列表的性能可能會降低,由於Redis在操做這種結構的時候,編解碼會出現必定的壓力。

壓縮列表的長度限制在500-2000以內,每一個元素體積限制在128字節或如下,壓縮列表的的性能都會處於合理範圍以內。

 

參考資料:

《redis設計與實現》

相關文章
相關標籤/搜索