文章目錄
- 初識列表
- 常用命令
- lpush
- lpushx
- lrange
- rpush
- rpushx
- lpop & rpop
- lindex
- linsert
- llen
- 阻塞操作 —— blpop & brpop
- 內部編碼
- 應用場景
初識列表
列表類型,用于存儲多個字符串。在操作和實現上,類似 C++ 的雙端隊列,支持隨機訪問(O(N)),頭插頭刪尾插尾刪(O(1)),詳細可參看【C++】deque 雙端隊列
大致實現為,使用一個數組,其中每個元素都是一個數組指針。從數組中間開始插入元素,頭插則從中間往前插入,尾插則從中間往后插入
Redis 實現的列表如下圖,有相對順序,規定左邊為頭,下標從0開始,如下圖。每個元素從左到右構成一個有序的列表,此處的有序是有順序,不是排序。列表的每個字符串稱為元素
,允許有重復的值,一個列表最多存儲 232 - 1 個元素。
功能上,支持從兩端插入(push)和彈出(pop),還可以獲取指定范圍的元素列表,獲取指定索引下標的元素,常被用于實現棧
或隊列
常用命令
lpush
將一個或多個元素從左側插入(頭插)到 list 中。left push
lpush key element [element …]
返回值:完成插入操作后,list 的長度
示例:
127.0.0.1:6379> lpush list1 1 2 3 4
(integer) 4
lpushx
在 key 存在時才能將一個或多個元素從左側插入(頭插)到 list 中,不存在 key 則直接返回。left push exist
lpushx key element [element …]
返回值:完成插入操作后,list 的長度
示例:
127.0.0.1:6379> lpushx list1 6 7 8 9
(integer) 8
127.0.0.1:6379> lpushx list2 1234
(integer) 0
lrange
獲取指定范圍的 list 的元素,從 start 到 stop,左閉右閉
其中 start 和 stop 的值可正可負,負數則表示倒數,-1 表示倒數第一個數,-2 代表倒數第二個數,以此類推
PS:lrange 是 list range
,而不是 left range,而是 ,所以沒有 rrange
lrange key start stop
返回值:指定區間的元素
備注:如果指定的區間不存在或無值,則返回空。若指定的區間有部分越界,并不會報錯,而是把能返回的值返回
示例:
127.0.0.1:6379> lrange list2 0 -1
(empty array)
127.0.0.1:6379> lrange list1 0 -1
1) "9"
2) "8"
3) "7"
4) "6"
5) "4"
6) "3"
7) "2"
8) "1"
127.0.0.1:6379> lrange list1 100 300
(empty array)
127.0.0.1:6379> lrange list1 0 300
1) "9"
2) "8"
3) "7"
4) "6"
5) "4"
6) "3"
7) "2"
8) "1"
因為插入元素時使用的是 lpush 和 lpushx,會將元素從左往右依次插入
rpush
將一個或者多個元素從右側插入(尾插),right push
rpushx key element [element …]
返回值:插入后 list 的長度
示例:
127.0.0.1:6379> rpush key2 1 2 3 4
(integer) 4
127.0.0.1:6379> lrange key2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
rpushx
在 key 存在時才能將一個或多個元素從右側插入(尾插)到 list 中,不存在 key 則直接返回。reft push exist
rpushx key element [element …]
返回值:完成插入操作后,list 的長度
示例:
127.0.0.1:6379> rpush key2 6 7 8 9
(integer) 8
127.0.0.1:6379> lrange key2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "6"
6) "7"
7) "8"
8) "9"
lpop & rpop
lpop
從 list 的左側取出元素(頭刪),left pop
lpop key
返回值:取出的元素或者 nil
示例:
127.0.0.1:6379> lrange list1 0 -1
1) "9"
2) "8"
3) "7"
4) "6"
5) "4"
6) "3"
7) "2"
8) "1"
127.0.0.1:6379> lpop list1
"9"
127.0.0.1:6379> lpop list1
"8"
127.0.0.1:6379> lpop list1
"7"
127.0.0.1:6379> lpop list1
"6"
127.0.0.1:6379> lrange list1 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
rpop
從 list 右側取出元素(尾刪),right pop
rpop key
返回值:取出的元素或 nil
示例:
127.0.0.1:6379> lrange list1 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> rpop list1
"1"
127.0.0.1:6379> rpop list1
"2"
127.0.0.1:6379> rpop list1
"3"
127.0.0.1:6379> rpop list1
"4"
127.0.0.1:6379> rpop list1
(nil)
127.0.0.1:6379> lrange list1 0 -1
(empty array)
lindex
獲取從左往右數下標為 index 的元素,list index
index 支持正數或負數,負數則表示倒數第 index 個
lindex key index
返回值:取出的元素或者 nil
示例:
127.0.0.1:6379> lrange key2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "6"
6) "7"
7) "8"
8) "9"
127.0.0.1:6379> lindex key2 0
"1"
127.0.0.1:6379> lindex key2 1
"2"
127.0.0.1:6379> lindex key2 -1
"9"
127.0.0.1:6379> lindex key2 -2
"8"
linsert
在特定位置插入元素,list insert
linsert key <before | after> pivot element
返回值:插入后 list 的長度
備注:pivot 是列表中存在的數,可選擇在其前插入或其后插入,若列表不存在該數,則返回 -1。如果列表存在多個 pivot,因為是從左往右遍歷,只會找到第一個
示例:
127.0.0.1:6379> lrange key1 0 -1
1) "7"
2) "6"
3) "5"
4) "3"
5) "4"
6) "3"
7) "2"
8) "1"
127.0.0.1:6379> linsert key1 before 3 100
(integer) 9
127.0.0.1:6379> lrange key1 0 -1
1) "7"
2) "6"
3) "5"
4) "100"
5) "3"
6) "4"
7) "3"
8) "2"
9) "1"
127.0.0.1:6379> linsert key1 after 3 200
(integer) 10
127.0.0.1:6379> lrange key1 0 -11) "7"2) "6"3) "5"4) "100"5) "3"6) "200"7) "4"8) "3"9) "2"
10) "1"
127.0.0.1:6379> linsert key1 after 10 200
(integer) -1
llen
獲取 list 的長度,list len
llen key
返回值:list 的長度
示例:
127.0.0.1:6379> lrange key1 0 -11) "7"2) "6"3) "5"4) "100"5) "3"6) "200"7) "4"8) "3"9) "2"
10) "1"
127.0.0.1:6379> llen key1
(integer) 10
阻塞操作 —— blpop & brpop
blpop
和 brpop
是 lpop 和 rpop 的阻塞版本,block left pop 和 block right pop
blpop key [key …] timeout
brpop key [key …] timeout
作用基本一致,但有以下區別:
- 在列表有元素的情況下,阻塞版本和非阻塞的表現是一致的,都直接取出元素并返回。但如果列表沒有元素,非阻塞版本會直接返回 nil,而阻塞版本會根據
timeout
,阻塞一段時間,期間 Redis 服務端可以執行其他命令,但執行該命令的客戶端會進行阻塞狀態 - blpop 和 brpop 可以支持取出多個列表的元素,如果設置了多個列表,那么會從左往右依次嘗試進行取出操作,一旦有一個列表不為空,可以彈出元素,則進行彈出操作并返回元素。
即并不是彈出多個列表的元素,而是從這些列表中彈出一個元素
- 如果多個客戶端同時對同一個列表進行阻塞彈出操作,則最先執行命令的客戶端會先嘗試進行彈出操作,如果列表為空,則都阻塞,直到列表有元素,也是最先執行命令的客戶端可以彈出元素
- timeout 單位為秒
示例:
127.0.0.1:6379> keys *
1) "key2"
2) "key1"
127.0.0.1:6379> lrange key1 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> lrange key2 0 -1
1) "1"
2) "1"
3) "11"
127.0.0.1:6379> blpop key1 key2 10
1) "key1"
2) "4"
127.0.0.1:6379> blpop key3 key2 10
1) "key2"
2) "1"
內部編碼
在舊版本時,列表的內部編碼有兩種:
- ziplist(壓縮列表):當列表的元素個數小于
list-max-ziplist-entries
配置(默認 512 個),同時列表中每個元素的長度都小于list-max-ziplist-value
配置(默認 64 字節)時,Redis 會選用 ziplist 來作為列表的內部編碼實現來減少內存消耗。 - linkedlist(鏈表):當列表類型無法滿足 ziplist 的條件時,Redis 會使用 linkedlist 作為列表的內部實現。
在較新版本,列表的內部編碼統一使用 quicklist
實現
127.0.0.1:6379> object encoding key1
"quicklist"
應用場景
模擬棧或隊列
列表使用雙端隊列,兩端的插入和刪除很高效,而棧和隊列都是操作受限的隊列,雙端隊列非常適合實現,C++ 的 STL 也是如此,queue 和 stack 的底層都使用 deque(雙端隊列)實現
- 當只使用同一端操作時,即
lpush & lpop
或rpush & rpop
即可模擬棧 - 當只使用對端相反操作時,即
lpush & rpop
或rpush & lpop
即可模擬隊列
消息隊列
如下圖所示
Redis 可使用 lpush + brpop
命令組合實現經典的阻塞式生產者 - 消費者模型隊列,生產者客戶端使用 lpush 從列表左側插入元素,多個消費者客戶端使用 brpop 命令,阻塞式地從隊列中取出元素。通過多個客戶端來保證消費的負載均衡和高可用性
分頻道的消息隊列
如圖下圖所示
Redis 同樣使用 lpush + brpop
命令,但通過不同的鍵模擬頻道的概念,不同的消費者可以通過 brpop 獲取不同的鍵值,實現訂閱不同頻道的理念。
以上就是本篇博客的所有內容,感謝你的閱讀
如果覺得本篇文章對你有所幫助的話,不妨點個贊支持一下博主,拜托啦,這對我真的很重要。