List-列表類型:L&R
列表類型:有序、可重復
Arraylist和linkedlist的區(qū)別
Arraylist是使用數(shù)組來存儲數(shù)據,特點:查詢快、增刪慢
Linkedlist是使用雙向鏈表存儲數(shù)據,特點:增刪快、查詢慢,但是查詢鏈表兩端的數(shù)據也很快。
Redis的list是采用來鏈表來存儲的,所以對于redis的list數(shù)據類型的操作,是操作list的兩端數(shù)據來操作的。
命令
向列表左邊增加元素:lpush key:val1 val2
127.0.0.1:6379>lpush list:1 1 2 3 (integer) 3
向列表右邊增加元素:rpush key:val1 val2
127.0.0.1:6379>rpush list:1 4 5 6 (integer) 3
查看列表:LRANGE key start stop
LRANGE命令是列表類型最常用的命令之一,獲取列表中的某一片段,將返回start、stop之間的所有元素(包含兩端的元素),索引從0開始。索引可以是負數(shù),如:“-1”代表最后邊的一個元素。
語法:LRANGE key start stop 獲取下標0-2的元素 前閉后閉 127.0.0.1:6379> lrange list1 0 2 1) "2" 2) "1" 3) "4" ? 這里需要注意的是,list的索引從0開始大家知道,但是取全部list的值時,結束索引需要設置為-1,也可以設置成list長度減1 127.0.0.1:6379> lrange l:list 0 -1 1) "6" 2) "5" 3) "2" 4) "2"
從列表兩端彈出元素:LPOP key&RPOP key
LPOP命令從列表左邊彈出一個元素,會分兩步完成:
第一步是將列表左邊的元素從列表中移除
第二步是返回被移除的元素值。
語法: LPOP key RPOP key 127.0.0.1:6379> lpop list:1 "3“ 127.0.0.1:6379> rpop list:1 "6“
獲取列表中元素的個數(shù)LLEN key
語法:LLEN key 127.0.0.1:6379> llen list:1 (integer) 2
刪除列表中指定的值 LREM key count value
LREM命令會刪除列表中前count個值為value的元素,返回實際刪除的元素個數(shù)。根據count值的不同,該命令的執(zhí)行方式會有所不同: 當count>0時, LREM會從列表左邊開始刪除。 當count<0時, LREM會從列表后邊開始刪除。 當count=0時, LREM刪除所有值為value的元素。 刪除2個值為1的元素 語法:LREM key count value 127.0.0.1:6379> lrem list1 2 1 (integer) 2
獲得/設置指定索引的元素值LINDEX key index
獲得指定索引的元素值 語法:LINDEX key index 127.0.0.1:6379> lindex list 2 "1" ? 設置指定索引的元素值 語法:LSET key index value 127.0.0.1:6379> lset list 2 2 OK
只保留列表指定范圍元素:LTRIM key start stop
只保留start - stop 之間的元素 語法:LTRIM key start stop 127.0.0.1:6379> lrange l:list 0 -1 1) "6" 2) "5" 3) "0" 4) "2" 127.0.0.1:6379> ltrim l:list 0 2 OK 127.0.0.1:6379> lrange l:list 0 -1 1) "6" 2) "5" 3) "0"
向列表中插入元素LINSERT key BEFORE|AFTER pivot value
該命令首先會在列表中從左到右查找值為pivot的元素,然后根據第二個參數(shù)是BEFORE還是AFTER來決定將value插入到該元素的前面還是后面。 ? 語法:LINSERT key BEFORE|AFTER pivot value 127.0.0.1:6379> lrange list 0 -1 1) "3" 2) "2" 3) "1" ? 將值為4的元素插入到3后面 127.0.0.1:6379> linsert list after 3 4 (integer) 4 127.0.0.1:6379> lrange list 0 -1 1) "3" 2) "4" 3) "2" 4) "1"
將A列表最后一個元素移到B列表RPOPLPUSH source destination
用于移除列表的最后一個元素,并將該元素添加到另一個列表并返回。
語法:RPOPLPUSH source destination ? 127.0.0.1:6379> lrange list 0 -1 1) "3" 2) "4" 3) "2" 4) "1" 127.0.0.1:6379> rpoplpush list newlist "1" 127.0.0.1:6379> lrange newlist 0 -1 1) "1" 127.0.0.1:6379> lrange list 0 -1 1) "3" 2) "4" 3) "2"
移出并獲取列表的最后一個元素(阻塞):Brpop
移出并獲取列表的最后一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發(fā)現(xiàn)可彈出元素為止。
redis 127.0.0.1:6379> BRPOP LIST1 LIST2 .. LISTN TIMEOUT
假如在指定時間內沒有任何元素被彈出,則返回一個 nil 和等待時長。 反之,返回一個含有兩個元素的列表,第一個元素是被彈出元素所屬的 key ,第二個元素是被彈出元素的值。
redis> DEL list1 list2 (integer) 0 redis> RPUSH list1 a b c (integer) 3 redis> BRPOP list1 list2 0 1) "list1" 2) "c"
將A列表最后一個元素移到B列表(阻塞)Brpoplpush
命令從列表中取出最后一個元素,并插入到另外一個列表的頭部; 如果列表沒有元素會阻塞列表直到等待超時或發(fā)現(xiàn)可彈出元素為止。假如在指定時間內沒有任何元素被彈出,則返回一個 nil 和等待時長。 反之,返回一個含有兩個元素的列表,第一個元素是被彈出元素的值,第二個元素是等待時長。
``` redis 127.0.0.1:6379> BRPOPLPUSH LIST1 ANOTHER_LIST TIMEOUT
非空列表
? redis> BRPOPLPUSH msg reciver 500 "hello moto" ? ? ? ? ? ? ? ? ? ? ? # 彈出元素的值 (3.38s) ? ? ? ? ? ? ? ? ? ? ? ? ? ? # 等待時長 ? redis> LLEN reciver (integer) 1 ? redis> LRANGE reciver 0 0 1) "hello moto" ? ?
空列表
? redis> BRPOPLPUSH msg reciver 1 (nil) (1.34s) ```
應用場景:大容量&更新頻率不高&的靜態(tài)列表
商品評論列表
思路
在Redis中創(chuàng)建商品評論列表
用戶發(fā)布商品評論,將評論信息轉成json存儲到list中。
用戶在頁面查詢評論列表,從redis中取出json數(shù)據展示到頁面。
定義商品評論列表key
商品編號為1001的商品評論key【items: comment:1001】
每日排行榜:某個時間段數(shù)據固定榜單
list類型的lrange命令可以分頁查看隊列中的數(shù)據??蓪⒚扛粢欢螘r間計算一次的排行榜存儲在list類型中,如京東每日的手機銷量排行、學校每次月考學生的成績排名、斗魚年終盛典主播排名等,下圖是酷狗音樂“K歌擂臺賽”的昨日打擂金曲排行榜,每日計算一次,存儲在list類型中,接口訪問時,通過page和size分頁獲取打擂金曲。
list類型的lpush命令和lrange命令能實現(xiàn)最新列表的功能,每次通過lpush命令往列表里插入新的元素,然后通過lrange命令讀取最新的元素列表,如朋友圈的點贊列表、評論列表。 ? 但是,并不是所有的最新列表都能用list類型實現(xiàn),因為對于頻繁更新的列表,list類型的分頁可能導致列表元素重復或漏掉,舉個例子,當前列表里由表頭到表尾依次有(E,D,C,B,A)五個元素,每頁獲取3個元素,用戶第一次獲取到(E,D,C)三個元素,然后表頭新增了一個元素F,列表變成了(F,E,D,C,B,A),此時用戶取第二頁拿到(C,B,A),元素C重復了??梢晕膊孱^拿 即 左邊插入從右邊拿。 只有不需要分頁(比如每次都只取列表的前5個元素)或者更新頻率低(比如每天凌晨更新一次)的列表才適合用list類型實現(xiàn)。 對于需要分頁并且會頻繁更新的列表,需用使用有序集合sorted set類型實現(xiàn)。 另外,需要通過時間范圍查找的最新列表,list類型也實現(xiàn)不了,也需要通過有序集合sorted set類型實現(xiàn),如以成交時間范圍作為條件來查詢的訂單列表。之后在介紹有序集合sorted set類型的應用場景時會詳細介紹sorted set類型如何實現(xiàn)最新列表。 ? 那么問題來了,對于排行榜和最新列表兩種應用場景,list類型能做到的sorted set類型都能做到,list類型做不到的sorted set類型也能做到,那為什么還要使用list類型去實現(xiàn)排行榜或最新列表呢,直接用sorted set類型不是更好嗎? 原因是sorted set類型占用的內存容量是list類型的數(shù)倍之多(之后會在容量章節(jié)詳細介紹),對于列表數(shù)量不多的情況,可以用sorted set類型來實現(xiàn),比如上文中舉例的打擂金曲排行榜,每天全國只有一份,兩種數(shù)據類型的內存容量差距可以忽略不計,但是如果要實現(xiàn)某首歌曲的翻唱作品地區(qū)排行榜,數(shù)百萬的歌曲,300多個地區(qū),會產生數(shù)量龐大的榜單,或者數(shù)量更加龐大的朋友圈點贊列表,就需要慎重地考慮容量的問題了。
底層數(shù)據結構壓縮列表和雙向鏈表
在版本3.2之前,Redis 列表list使用兩種數(shù)據結構作為底層實現(xiàn):
- 壓縮列表ziplist
- 雙向鏈表linkedlist
- 因為雙向鏈表占用的內存比壓縮列表要多, 所以當創(chuàng)建新的列表鍵時, 列表會優(yōu)先考慮使用壓縮列表, 并且在有需要的時候, 才從壓縮列表實現(xiàn)轉換到雙向鏈表實現(xiàn)。
ziplist
ziplist 是一個特殊的雙向鏈表 特殊之處在于:沒有維護雙向指針:prev next;而是存儲上一個 entry的長度和 當前entry的長度,通過長度推算下一個元素在什么地方。 犧牲讀取的性能,獲得高效的存儲空間,因為(簡短字符串的情況)存儲指針比存儲entry長度 更費內存。這是典型的“時間換空間”。
ziplist使用局限性 字段、值比較小,才會用ziplist。
壓縮列表ziplist存儲結構 ziplist使用連續(xù)的內存塊,每一個節(jié)點(entry)都是連續(xù)存儲的;
1.ziplist可以通過data header計算出當前entry的結束位置,也就能得到下一個entry的起始位置,正向遍歷
2.ziplist可以通過length of previous entry計算出上一個entry的起始位置,反向遍歷
壓縮列表轉化成雙向鏈表條件
創(chuàng)建新列表時 redis 默認使用 redisencodingziplist 編碼, 當以下任意一個條件被滿足時, 列表會被轉換成 redisencodinglinkedlist 編碼:
- 試圖往列表新添加一個字符串值,且這個字符串的長度超過 server.listmaxziplist_value (默認值為 64字節(jié))。
- ziplist 包含的節(jié)點超過 server.listmaxziplist_entries (默認值為 512 )。
注意:這兩個條件是可以修改的,在 redis.conf 中:
list-max-ziplist-value 64 list-max-ziplist-entries 512
文章來源:http://www.zghlxwxcb.cn/news/detail-668369.html
linkedlist
當鏈表entry數(shù)據超過512、或單個value 長度超過64字節(jié),底層就會轉化成linkedlist編碼; linkedlist是標準的雙向鏈表,Node節(jié)點包含prev和next指針,可以進行雙向遍歷; 還保存了 head 和 tail 兩個指針,因此,對鏈表的表頭和表尾進行插入的復雜度都為 (1) —— 這是高效實現(xiàn) LPUSH 、 RPOP、 RPOPLPUSH 等命令的關鍵。 文章來源地址http://www.zghlxwxcb.cn/news/detail-668369.html
到了這里,關于3.redis數(shù)據結構之List的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!