Redis 列表對(duì)象

2018-08-02 14:48 更新

列表對(duì)象的編碼可以是 ziplist 或者 linkedlist 。

ziplist 編碼的列表對(duì)象使用壓縮列表作為底層實(shí)現(xiàn), 每個(gè)壓縮列表節(jié)點(diǎn)(entry)保存了一個(gè)列表元素。

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

redis> RPUSH numbers 1 "three" 5
(integer) 3

如果 numbers 鍵的值對(duì)象使用的是 ziplist 編碼, 這個(gè)這個(gè)值對(duì)象將會(huì)是圖 8-5 所展示的樣子。

另一方面, linkedlist 編碼的列表對(duì)象使用雙端鏈表作為底層實(shí)現(xiàn), 每個(gè)雙端鏈表節(jié)點(diǎn)(node)都保存了一個(gè)字符串對(duì)象, 而每個(gè)字符串對(duì)象都保存了一個(gè)列表元素。

舉個(gè)例子, 如果前面所說的 numbers 鍵創(chuàng)建的列表對(duì)象使用的不是 ziplist 編碼, 而是 linkedlist 編碼, 那么 numbers 鍵的值對(duì)象將是圖 8-6 所示的樣子。

注意, linkedlist 編碼的列表對(duì)象在底層的雙端鏈表結(jié)構(gòu)中包含了多個(gè)字符串對(duì)象, 這種嵌套字符串對(duì)象的行為在稍后介紹的哈希對(duì)象、集合對(duì)象和有序集合對(duì)象中都會(huì)出現(xiàn), 字符串對(duì)象是 Redis 五種類型的對(duì)象中唯一一種會(huì)被其他四種類型對(duì)象嵌套的對(duì)象。

注意

為了簡(jiǎn)化字符串對(duì)象的表示, 我們?cè)趫D 8-6 使用了一個(gè)帶有 StringObject 字樣的格子來表示一個(gè)字符串對(duì)象, 而 StringObject 字樣下面的是字符串對(duì)象所保存的值。

比如說, 圖 8-7 代表的就是一個(gè)包含了字符串值 "three" 的字符串對(duì)象, 它是 8-8 的簡(jiǎn)化表示。

本書接下來的內(nèi)容將繼續(xù)沿用這一簡(jiǎn)化表示。

編碼轉(zhuǎn)換

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

  1. 列表對(duì)象保存的所有字符串元素的長(zhǎng)度都小于 64 字節(jié);
  2. 列表對(duì)象保存的元素?cái)?shù)量小于 512 個(gè);

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

注意

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

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

以下代碼展示了列表對(duì)象因?yàn)楸4媪碎L(zhǎng)度太大的元素而進(jìn)行編碼轉(zhuǎn)換的情況:

# 所有元素的長(zhǎng)度都小于 64 字節(jié)
redis> RPUSH blah "hello" "world" "again"
(integer) 3

redis> OBJECT ENCODING blah
"ziplist"

# 將一個(gè) 65 字節(jié)長(zhǎng)的元素推入列表對(duì)象中
redis> RPUSH blah "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"
(integer) 4

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

除此之外, 以下代碼展示了列表對(duì)象因?yàn)楸4娴脑財(cái)?shù)量過多而進(jìn)行編碼轉(zhuǎn)換的情況:

# 列表對(duì)象包含 512 個(gè)元素
redis> EVAL "for i=1,512 do redis.call('RPUSH', KEYS[1], i) end" 1 "integers"
(nil)

redis> LLEN integers
(integer) 512

redis> OBJECT ENCODING integers
"ziplist"

# 再向列表對(duì)象推入一個(gè)新元素,使得對(duì)象保存的元素?cái)?shù)量達(dá)到 513 個(gè)
redis> RPUSH integers 513
(integer) 513

# 編碼已改變
redis> OBJECT ENCODING integers
"linkedlist"

列表命令的實(shí)現(xiàn)

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


表 8-8 列表命令的實(shí)現(xiàn)

命令 ziplist 編碼的實(shí)現(xiàn)方法 linkedlist 編碼的實(shí)現(xiàn)方法
LPUSH 調(diào)用 ziplistPush 函數(shù), 將新元素推入到壓縮列表的表頭。 調(diào)用 listAddNodeHead 函數(shù), 將新元素推入到雙端鏈表的表頭。
RPUSH 調(diào)用 ziplistPush 函數(shù), 將新元素推入到壓縮列表的表尾。 調(diào)用 listAddNodeTail 函數(shù), 將新元素推入到雙端鏈表的表尾。
LPOP 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表的表頭節(jié)點(diǎn), 在向用戶返回節(jié)點(diǎn)所保存的元素之后, 調(diào)用ziplistDelete 函數(shù)刪除表頭節(jié)點(diǎn)。 調(diào)用 listFirst 函數(shù)定位雙端鏈表的表頭節(jié)點(diǎn), 在向用戶返回節(jié)點(diǎn)所保存的元素之后, 調(diào)用 listDelNode 函數(shù)刪除表頭節(jié)點(diǎn)。
RPOP 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表的表尾節(jié)點(diǎn), 在向用戶返回節(jié)點(diǎn)所保存的元素之后, 調(diào)用ziplistDelete 函數(shù)刪除表尾節(jié)點(diǎn)。 調(diào)用 listLast 函數(shù)定位雙端鏈表的表尾節(jié)點(diǎn), 在向用戶返回節(jié)點(diǎn)所保存的元素之后, 調(diào)用 listDelNode 函數(shù)刪除表尾節(jié)點(diǎn)。
LINDEX 調(diào)用 ziplistIndex 函數(shù)定位壓縮列表中的指定節(jié)點(diǎn), 然后返回節(jié)點(diǎn)所保存的元素。 調(diào)用 listIndex 函數(shù)定位雙端鏈表中的指定節(jié)點(diǎn), 然后返回節(jié)點(diǎn)所保存的元素。
LLEN 調(diào)用 ziplistLen 函數(shù)返回壓縮列表的長(zhǎng)度。 調(diào)用 listLength 函數(shù)返回雙端鏈表的長(zhǎng)度。
LINSERT 插入新節(jié)點(diǎn)到壓縮列表的表頭或者表尾時(shí), 使用ziplistPush 函數(shù); 插入新節(jié)點(diǎn)到壓縮列表的其他位置時(shí), 使用 ziplistInsert 函數(shù)。 調(diào)用 listInsertNode 函數(shù), 將新節(jié)點(diǎn)插入到雙端鏈表的指定位置。
LREM 遍歷壓縮列表節(jié)點(diǎn), 并調(diào)用 ziplistDelete 函數(shù)刪除包含了給定元素的節(jié)點(diǎn)。 遍歷雙端鏈表節(jié)點(diǎn), 并調(diào)用 listDelNode 函數(shù)刪除包含了給定元素的節(jié)點(diǎn)。
LTRIM 調(diào)用 ziplistDeleteRange 函數(shù), 刪除壓縮列表中所有不在指定索引范圍內(nèi)的節(jié)點(diǎn)。 遍歷雙端鏈表節(jié)點(diǎn), 并調(diào)用 listDelNode 函數(shù)刪除鏈表中所有不在指定索引范圍內(nèi)的節(jié)點(diǎn)。
LSET 調(diào)用 ziplistDelete 函數(shù), 先刪除壓縮列表指定索引上的現(xiàn)有節(jié)點(diǎn), 然后調(diào)用 ziplistInsert 函數(shù), 將一個(gè)包含給定元素的新節(jié)點(diǎn)插入到相同索引上面。 調(diào)用 listIndex 函數(shù), 定位到雙端鏈表指定索引上的節(jié)點(diǎn), 然后通過賦值操作更新節(jié)點(diǎn)的值。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)