type命令實際返回的就是當前鍵的數據結構類型,它們分別是:string(字符串)、hash(哈希)、
list(列表)、set(集合)、zset(有序集合),但這些只是Redis對外的數據結構。
實際上每種數據結構都有自己底層的內部編碼實現,而且是多種實現,這樣Redis會在合適的場景選擇合適的內部編碼。
可以看到每種數據結構都有兩種以上的內部編碼實現,例如string數據結構就包含了raw、int和embstr三種內部編碼。
同時,有些內部編碼可以作為多種外部數據結構的內部實現,例如ziplist就是hash、list和zset共有的內部編碼。
我們可以通過object encoding命令查詢內部編碼:
127.0.0.1:6379> set set:1 hello OK 127.0.0.1:6379> object encoding set:1 "embstr" 127.0.0.1:6379> hset user:1 name kebi (integer) 1 127.0.0.1:6379> object encoding user:1 "ziplist"
可以看到鍵set:1對應值的內部編碼是“embstr”,鍵user:1對應值的內部編碼是“ziplist”。
Redis這樣設計有兩個好處:
第一,可以改進內部編碼,而對外的數據結構和命令沒有影響,這樣一旦開發開發出優秀的內部編碼,無需改動外部數據結構和命令。
第二,多種內部編碼實現可以在不同場景下發揮各自的優勢。例如ziplist比較節省內存,但是在列表元素比較多的情況下,性能會有所下降,
這時候Redis會根據配置選項將列表類型的內部實現轉換為linkedlist。
下面會分別介紹5種數據結構的內部編碼方式。
1.字符串的內部編碼
字符串類型的內部編碼有3種:
- int:8個字節的長整型。
- embstr:小於等於39個字節的字符串。
- raw:大於39個字節的字符串。
Redis會根據當前值的類型和長度決定使用內部編碼實現。
(1)整數類型示例如下:
127.0.0.1:6379> set str 1234567 OK 127.0.0.1:6379> object encoding str "int"
(2)短字符串示例如下:
127.0.0.1:6379> set str "hello world" OK 127.0.0.1:6379> object encoding str "embstr"
(3)長字符串示例如下:
127.0.0.1:6379> set str "Tranquil,unbeatable to the outside. -- yangming" #“凝聚於內,無敵於外。--王陽明” OK 127.0.0.1:6379> object encoding str "raw"
2.哈希的內部編碼
哈希類型的內部編碼有兩種:
- ziplist(壓縮列表):當哈希類型元素個數小於hash-max-ziplist-entries配置(默認512個),
同時所有值都小於hash-max-ziplist-value配置(默認64個字節)時,Redis會使用ziplist作為哈希的內部實現
ziplist使用更加緊湊的結構實現多個元素的連續存儲,所以在節省內存方面比hashtable更加優秀。
- hashtable(哈希表):當哈希類型無法滿足ziplist的條件時,Redis會使用hashtable作為哈希的內部實現。
因為此時ziplist的讀寫效率會下降,而hashtable的讀寫時間復雜度為O(1)。
下面演示哈希類型的內部編碼,及相應的變化。
(1)當field個數比較少且沒有大的value時,內部編碼為ziplist:
127.0.0.1:6379> hmset user:2 name kebi age 26 OK 127.0.0.1:6379> object encoding user:2 "ziplist"
(2)當有value大於64個字節,內部編碼會由ziplist變為hashtable:
127.0.0.1:6379> hmset user:1 info "沐春風,惹一身紅塵;望秋月,化半縷輕煙。顧盼間乾坤倒轉,一霎時滄海桑田。方曉,彈指紅顏老,剎那芳華逝。" 127.0.0.1:6379> object encoding user:1 "hashtable"
(3)當field個數超過512,內部編碼也會由ziplist變為hashtable:
...待插入內容...
注意:當一個哈希的編碼由ziplist變為hashtable的時候,即使在替換掉所有值,它一直都會是hashtable類型。
3.列表的內部編碼
列表類型的內部編碼有兩種:
- ziplist(壓縮列表):當哈希類型元素個數小於hash-max-ziplist-entries配置(默認512個)
同時所有值都小於hash-max-ziplist-value配置(默認64個字節)時,Redis會使用ziplist作為哈希的內部實現。
- linkedlist(鏈表):當列表類型無法滿足ziplist的條件時,Redis會使用linkedlist作為列表的內部實現。
下面演示列表類型的內部編碼,以及相應的變化:
(1)當元素個數較少且沒有大元素時,內部編碼為ziplist:
127.0.0.1:6379> rpush list:2 a b c (integer) 3 127.0.0.1:6379> object encoding list:2 "ziplist"
(2)當元素個數超過512個,內部編碼變為linkedlist:
127.0.0.1:6379>lpush setkey 1 2 3 ... 513 OK 127.0.0.1:6379> object encoding listkey "linkedlist"
(3)當某個元素超過64個字節,內部編碼也會變為linkedlist:
127.0.0.1:6379> rpush list:1 a b "我不再說話,不再思索,但無盡的愛從靈魂中升起,我將遠行,走得很遠,如同一個吉普塞人,穿過大自然——幸福得如有一位女子同行。" (integer) 6 127.0.0.1:6379> object encoding list:1 "linkedlist"
#只能升級,不能自動變回ziplist類型
4.集合的內部編碼
集合類型的內部編碼有兩種:
- intset(整數集合):當集合中的元素都是整數且元素個數小於set-max-intset-entries配置(默認512個)時,
Redis會選用intset來作為集合內部實現,從而減少內存的使用。
- hashtable(哈希表):當集合類型無法滿足intset的條件時,Redis會使用hashtable作為集合的內部實現。
下面用示例來說明:
(1)當元素個數較少且都為整數時,內部編碼為intset:
127.0.0.1:6379> sadd setkey 2 3 4 5 (integer) 4 127.0.0.1:6379> object encoding setkey "intset"
(2)當元素個數超過512個,內部編碼變為hastable:
127.0.0.1:6379>sadd setkey2 1 2 3 4 5 6 7... 511 512 513 OK 127.0.0.1:6379> object encoding setkey2 "hashtable"
(3)當某個元素不為整數時,內部編碼也會變為hashtable:
127.0.0.1:6379> sadd setkey3 a b c (integer) 3 127.0.0.1:6379> object encoding setkey2 "hashtable"
5.有序集合的內部編碼
有序集合類型的內部編碼有兩種
- ziplist(壓縮列表):當有序集合的元素個數小於zset-max-ziplist-entries配置(默認128個)
同時每個元素的值小於zset-max-ziplist-value配置(默認64個字節)時,Redis會用ziplist來作為有序集合的內部實現,ziplist可以有效減少內存使用。
- skiplist(跳躍表):當ziplist條件不滿足時,有序集合會使用skiplist作為內部實現,因為此時zip的讀寫效率會下降。
下面用示例來說明:
(1)當元素個數較少且每個元素較小時,內部編碼為ziplist:
127.0.0.1:6379> zadd zsetkey 50 a 60 b 30 c (integer) 3 127.0.0.1:6379> object encoding zsetkey "ziplist"
(2)當元素個數超過128個,內部編碼變為skiplist:
...待輸入...
(3)當某個元素大於64個字節時,內部編碼也會變為skiplist:
127.0.0.1:6379> zadd zsetkey 50 a 60 b 30 '閃爍的太陽已越過高傲的山巒,幽谷中的光點有若泡沫浮起。' (integer) 1 127.0.0.1:6379> object encoding zsetkey "skiplist"