Redis 哈希對(duì)象

2018-08-02 14:48 更新

哈希對(duì)象的編碼可以是 ziplist 或者 hashtable 。

ziplist 編碼的哈希對(duì)象使用壓縮列表作為底層實(shí)現(xiàn), 每當(dāng)有新的鍵值對(duì)要加入到哈希對(duì)象時(shí), 程序會(huì)先將保存了鍵的壓縮列表節(jié)點(diǎn)推入到壓縮列表表尾, 然后再將保存了值的壓縮列表節(jié)點(diǎn)推入到壓縮列表表尾, 因此:

  • 保存了同一鍵值對(duì)的兩個(gè)節(jié)點(diǎn)總是緊挨在一起, 保存鍵的節(jié)點(diǎn)在前, 保存值的節(jié)點(diǎn)在后;
  • 先添加到哈希對(duì)象中的鍵值對(duì)會(huì)被放在壓縮列表的表頭方向, 而后來(lái)添加到哈希對(duì)象中的鍵值對(duì)會(huì)被放在壓縮列表的表尾方向。

舉個(gè)例子, 如果我們執(zhí)行以下 HSET 命令, 那么服務(wù)器將創(chuàng)建一個(gè)列表對(duì)象作為 profile 鍵的值:

redis> HSET profile name "Tom"
(integer) 1

redis> HSET profile age 25
(integer) 1

redis> HSET profile career "Programmer"
(integer) 1

如果 profile 鍵的值對(duì)象使用的是 ziplist 編碼, 那么這個(gè)值對(duì)象將會(huì)是圖 8-9 所示的樣子, 其中對(duì)象所使用的壓縮列表如圖 8-10 所示。

另一方面, hashtable 編碼的哈希對(duì)象使用字典作為底層實(shí)現(xiàn), 哈希對(duì)象中的每個(gè)鍵值對(duì)都使用一個(gè)字典鍵值對(duì)來(lái)保存:

  • 字典的每個(gè)鍵都是一個(gè)字符串對(duì)象, 對(duì)象中保存了鍵值對(duì)的鍵;
  • 字典的每個(gè)值都是一個(gè)字符串對(duì)象, 對(duì)象中保存了鍵值對(duì)的值。

舉個(gè)例子, 如果前面 profile 鍵創(chuàng)建的不是 ziplist 編碼的哈希對(duì)象, 而是 hashtable 編碼的哈希對(duì)象, 那么這個(gè)哈希對(duì)象應(yīng)該會(huì)是圖 8-11 所示的樣子。

編碼轉(zhuǎn)換

當(dāng)哈希對(duì)象可以同時(shí)滿足以下兩個(gè)條件時(shí), 哈希對(duì)象使用 ziplist 編碼:

  1. 哈希對(duì)象保存的所有鍵值對(duì)的鍵和值的字符串長(zhǎng)度都小于 64 字節(jié);
  2. 哈希對(duì)象保存的鍵值對(duì)數(shù)量小于 512 個(gè);

不能滿足這兩個(gè)條件的哈希對(duì)象需要使用 hashtable 編碼。

注意

這兩個(gè)條件的上限值是可以修改的, 具體請(qǐng)看配置文件中關(guān)于 hash-max-ziplist-value 選項(xiàng)和 hash-max-ziplist-entries 選項(xiàng)的說(shuō)明。

對(duì)于使用 ziplist 編碼的列表對(duì)象來(lái)說(shuō), 當(dāng)使用 ziplist 編碼所需的兩個(gè)條件的任意一個(gè)不能被滿足時(shí), 對(duì)象的編碼轉(zhuǎn)換操作就會(huì)被執(zhí)行: 原本保存在壓縮列表里的所有鍵值對(duì)都會(huì)被轉(zhuǎn)移并保存到字典里面, 對(duì)象的編碼也會(huì)從 ziplist 變?yōu)?nbsp;hashtable 。

以下代碼展示了哈希對(duì)象因?yàn)殒I值對(duì)的鍵長(zhǎng)度太大而引起編碼轉(zhuǎn)換的情況:

# 哈希對(duì)象只包含一個(gè)鍵和值都不超過(guò) 64 個(gè)字節(jié)的鍵值對(duì)
redis> HSET book name "Mastering C++ in 21 days"
(integer) 1

redis> OBJECT ENCODING book
"ziplist"

# 向哈希對(duì)象添加一個(gè)新的鍵值對(duì),鍵的長(zhǎng)度為 66 字節(jié)
redis> HSET book long_long_long_long_long_long_long_long_long_long_long_description "content"
(integer) 1

# 編碼已改變
redis> OBJECT ENCODING book
"hashtable"

除了鍵的長(zhǎng)度太大會(huì)引起編碼轉(zhuǎn)換之外, 值的長(zhǎng)度太大也會(huì)引起編碼轉(zhuǎn)換, 以下代碼展示了這種情況的一個(gè)示例:

# 哈希對(duì)象只包含一個(gè)鍵和值都不超過(guò) 64 個(gè)字節(jié)的鍵值對(duì)
redis> HSET blah greeting "hello world"
(integer) 1

redis> OBJECT ENCODING blah
"ziplist"

# 向哈希對(duì)象添加一個(gè)新的鍵值對(duì),值的長(zhǎng)度為 68 字節(jié)
redis> HSET blah story "many string ... many string ... many string ... many string ... many"
(integer) 1

# 編碼已改變
redis> OBJECT ENCODING blah
"hashtable"

最后, 以下代碼展示了哈希對(duì)象因?yàn)榘逆I值對(duì)數(shù)量過(guò)多而引起編碼轉(zhuǎn)換的情況:

# 創(chuàng)建一個(gè)包含 512 個(gè)鍵值對(duì)的哈希對(duì)象
redis> EVAL "for i=1, 512 do redis.call('HSET', KEYS[1], i, i) end" 1 "numbers"
(nil)

redis> HLEN numbers
(integer) 512

redis> OBJECT ENCODING numbers
"ziplist"

# 再向哈希對(duì)象添加一個(gè)新的鍵值對(duì),使得鍵值對(duì)的數(shù)量變成 513 個(gè)
redis> HMSET numbers "key" "value"
OK

redis> HLEN numbers
(integer) 513

# 編碼改變
redis> OBJECT ENCODING numbers
"hashtable"

哈希命令的實(shí)現(xiàn)

因?yàn)楣fI的值為哈希對(duì)象, 所以用于哈希鍵的所有命令都是針對(duì)哈希對(duì)象來(lái)構(gòu)建的, 表 8-9 列出了其中一部分哈希鍵命令, 以及這些命令在不同編碼的哈希對(duì)象下的實(shí)現(xiàn)方法。


表 8-9 哈希命令的實(shí)現(xiàn)

命令 ziplist 編碼實(shí)現(xiàn)方法 hashtable 編碼的實(shí)現(xiàn)方法
HSET 首先調(diào)用 ziplistPush 函數(shù), 將鍵推入到壓縮列表的表尾, 然后再次調(diào)用 ziplistPush 函數(shù), 將值推入到壓縮列表的表尾。 調(diào)用 dictAdd 函數(shù), 將新節(jié)點(diǎn)添加到字典里面。
HGET 首先調(diào)用 ziplistFind 函數(shù), 在壓縮列表中查找指定鍵所對(duì)應(yīng)的節(jié)點(diǎn), 然后調(diào)用 ziplistNext 函數(shù), 將指針移動(dòng)到鍵節(jié)點(diǎn)旁邊的值節(jié)點(diǎn), 最后返回值節(jié)點(diǎn)。 調(diào)用 dictFind 函數(shù), 在字典中查找給定鍵, 然后調(diào)用dictGetVal 函數(shù), 返回該鍵所對(duì)應(yīng)的值。
HEXISTS 調(diào)用 ziplistFind 函數(shù), 在壓縮列表中查找指定鍵所對(duì)應(yīng)的節(jié)點(diǎn), 如果找到的話說(shuō)明鍵值對(duì)存在, 沒(méi)找到的話就說(shuō)明鍵值對(duì)不存在。 調(diào)用 dictFind 函數(shù), 在字典中查找給定鍵, 如果找到的話說(shuō)明鍵值對(duì)存在, 沒(méi)找到的話就說(shuō)明鍵值對(duì)不存在。
HDEL 調(diào)用 ziplistFind 函數(shù), 在壓縮列表中查找指定鍵所對(duì)應(yīng)的節(jié)點(diǎn), 然后將相應(yīng)的鍵節(jié)點(diǎn)、 以及鍵節(jié)點(diǎn)旁邊的值節(jié)點(diǎn)都刪除掉。 調(diào)用 dictDelete 函數(shù), 將指定鍵所對(duì)應(yīng)的鍵值對(duì)從字典中刪除掉。
HLEN 調(diào)用 ziplistLen 函數(shù), 取得壓縮列表包含節(jié)點(diǎn)的總數(shù)量, 將這個(gè)數(shù)量除以 2 , 得出的結(jié)果就是壓縮列表保存的鍵值對(duì)的數(shù)量。 調(diào)用 dictSize 函數(shù), 返回字典包含的鍵值對(duì)數(shù)量, 這個(gè)數(shù)量就是哈希對(duì)象包含的鍵值對(duì)數(shù)量。
HGETALL 遍歷整個(gè)壓縮列表, 用 ziplistGet 函數(shù)返回所有鍵和值(都是節(jié)點(diǎn))。 遍歷整個(gè)字典, 用 dictGetKey 函數(shù)返回字典的鍵, 用dictGetVal 函數(shù)返回字典的值。
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)