一、List類型使用說明
-
list類型是用來存儲多個有序的字符串的,支持存儲2^32次方-1個元素。
-
redis可以從鏈表的兩端進行插入(pubsh)和彈出(pop)元素,充當隊列或者棧
-
支持讀取指定范圍的元素集
-
讀取指定下標的元素等
注意它是鏈表而不是數組。這意味着 list 的插入和刪除操作非常快,時間復雜度為 O(1),但是索引定位很慢,時間復雜度為 O(n)
另外當列表彈出了最后一個元素之后,該數據結構自動被刪除,內存被回收。
二、String類型常用命令:
右邊進左邊出:隊列
# 進入隊列 > rpush books python java golang (integer) 3 # 隊列長度 > llen books (integer) 3 # 取出隊列 > lpop books "python" > lpop books "java" > lpop books "golang" > lpop books (nil)
右邊進右邊出:棧
# 入棧 > rpush books python java golang (integer) 3 # 出棧 > rpop books "golang" > rpop books "java" > rpop books "python" > rpop books (nil)
慢操作
lindex 相當於 Java 鏈表的get(int index)方法,它需要對鏈表進行遍歷,性能隨着參數index增大而變差。
> rpush books python java golang (integer) 3 > lindex books 1 # O(n) 慎用 "java" > lrange books 0 -1 # 獲取所有元素,O(n) 慎用 1) "python" 2) "java" 3) "golang" > ltrim books 1 0 # O(n) 慎用 這其實是清空了整個列表,因為區間范圍長度為負 OK > llen books (integer) 0
ltrim 和字面意思不太一樣,與其說去除不如說保留。
因為 ltrim 兩個參數start_index和end_index定義了一個區間內的值將被保留下來。這使它非常適合實現一個定長的鏈表。擴展:Redis面試連環問,快看看你能走到哪一步!
三、使用場景:鏈表用來做異步隊列
鏈表常用來做異步隊列使用
-
將需要延后處理的任務結構體序列化(JSON)成字符串塞進 Redis 的列表
-
另一個線程從這個列表中輪詢數據進行處理。
-
lpush + lpop = stack 先進后出的棧
-
lpush + rpop = queue 先進先出的隊列
-
lpush + ltrim = capped collection 有限集合
-
lpush + brpop = message queue 消息隊列
Redis 隊列繞不開的消息丟失問題
一般借助List來實現消息隊列:
-
通過命令LPUSH(BLPUSH)把消息入隊
-
通過命令RPOP(BRPOP)獲取消息。
但這種方式實現的隊列是不安全的。
因為RPOP(BRPOP)命令的特性:
-
移除list的隊尾元素(消息)並返回給客戶端。這時該元素只存在於客戶端的上下文中,redis服務器中沒有這個元素.
-
如果客戶端在處理元素的過程崩潰了,那么這個元素就永遠丟失了。這種情況導致:客戶端雖然成功收到了消息,但是卻沒有處理它。
試圖搶救一下
那怎么來實現一個更安全的隊列呢?
可以試試redis的RPOPLPUSH (或者其阻塞版本的 BRPOPLPUSH)命令。
具體是操作是:
-
在A隊列推出元素(並刪除)時,保存元素到 B隊列。
-
如果處理 元素 的客戶端奔潰了,還可以在B隊列找到
redis> RPUSH mylist "one" (integer) 1 redis> RPUSH mylist "two" (integer) 2 redis> RPUSH mylist "three" (integer) 3 redis> RPOPLPUSH mylist myotherlist "three" redis> LRANGE mylist 0 -1 1) "one" 2) "two" redis> LRANGE myotherlist 0 -1 1) "three" redis>
這種方法存在兩個問題,
-
多個消費者同時將消息轉存入第二個隊列,第二隊列會出現( 已執行、未執行 )消息堆積
-
假設你的消息很特別,內容不會重復,你可以通過lrem a 0 "元素"函數找到並刪除消息,另外啟動的那個專門處理第二個隊列的client面對的隊列中的信息數量必須很小,如果很大client處理不過來又不能使用並發,因為使用並發必須將消息pop出隊列2,如果pop出隊列2,那就又回到了我們本來要繞開的問題。
最后
所以折騰試試,發現redislist
-
做消費者確認ACK麻煩
-
不能重復消費,一旦消費就會被刪除
-
隊列不去重