前言
列表(list)類型是用來存儲多個(gè)有序的字符串,列表中的每個(gè)字符串稱為元素(element)瑟枫,一個(gè)列表最多可以存儲232-1個(gè)元素。在Redis中,可以對列表兩端插入(push)和彈出(pop)捅僵,還可以獲取指定范圍的元素列表、獲取指定索引下標(biāo)的元素等眨层。列表是一種比較靈活的數(shù)據(jù)結(jié)構(gòu)庙楚,它可以充當(dāng)棧和隊(duì)列的角色,在實(shí)際開發(fā)上有很多應(yīng)用場景趴樱。
列表類型有兩個(gè)特點(diǎn):
- 列表中的元素是有序的馒闷,這就意味著可以通過索引下標(biāo)獲取某個(gè)元素或者某個(gè)范圍內(nèi)的元素列表。
- 列表中的元素可以是重復(fù)的叁征。
一纳账、內(nèi)部實(shí)現(xiàn)
在Redis3.2版本以前列表類型的內(nèi)部編碼有兩種。
- ziplist(壓縮列表):當(dāng)列表的元素個(gè)數(shù)小于list-max-ziplist-entries配置(默認(rèn)512個(gè))捺疼,同時(shí)列表中每個(gè)元素的值都小于list-max-ziplist-value配置時(shí)(默認(rèn)64字節(jié))疏虫,Redis會選用ziplist來作為列表的內(nèi)部實(shí)現(xiàn)來減少內(nèi)存的使用。
- linkedlist(鏈表):當(dāng)列表類型無法滿足ziplist的條件時(shí)啤呼,Redis會使用linkedlist作為列表的內(nèi)部實(shí)現(xiàn)卧秘。
- 而在Redis3.2版本開始對列表數(shù)據(jù)結(jié)構(gòu)進(jìn)行了改造,使用 quicklist 代替了 ziplist 和 linkedlist官扣。
二翅敌、常用命令
Redis列表對象常用命令如下表(點(diǎn)擊命令可查看命令詳細(xì)說明)。
命令 | 說明 | 時(shí)間復(fù)雜度 |
---|---|---|
BLPOP key [key …] timeout | 刪除醇锚,并獲得該列表中的第一元素哼御,或阻塞坯临,直到有一個(gè)可用 | O(1) |
BRPOP key [key …] timeout | 刪除,并獲得該列表中的最后一個(gè)元素恋昼,或阻塞看靠,直到有一個(gè)可用 | O(1) |
BRPOPLPUSH source destination timeout | 彈出一個(gè)列表的值,將它推到另一個(gè)列表液肌,并返回它;或阻塞挟炬,直到有一個(gè)可用 | O(1) |
LINDEX key index | 獲取一個(gè)元素,通過其索引列表 | O(N) |
LINSERT key BEFORE | AFTER pivot value在列表中的另一個(gè)元素之前或之后插入一個(gè)元素 | O(N) |
LLEN key | 獲得隊(duì)列(List)的長度 | O(1) |
LPOP key | 從隊(duì)列的左邊出隊(duì)一個(gè)元素 | O(1) |
LPUSH key value [value …] | 從隊(duì)列的左邊入隊(duì)一個(gè)或多個(gè)元素 | O(1) |
LPUSHX key value | 當(dāng)隊(duì)列存在時(shí)嗦哆,從隊(duì)到左邊入隊(duì)一個(gè)元素 | O(1) |
LRANGE key start stop | 從列表中獲取指定返回的元素 | O(S+N) |
LREM key count value | 從列表中刪除元素 | O(N) |
LSET key index value | 設(shè)置隊(duì)列里面一個(gè)元素的值 | O(N) |
LTRIM key start stop | 修剪到指定范圍內(nèi)的清單 | O(N) |
RPOP key | 從隊(duì)列的右邊出隊(duì)一個(gè)元 | O(1) |
RPOPLPUSH source destination | 刪除列表中的最后一個(gè)元素谤祖,將其追加到另一個(gè)列表 | O(1) |
RPUSH key value [value …] | 從隊(duì)列的右邊入隊(duì)一個(gè)元素 | O(1) |
RPUSHX key value | 從隊(duì)列的右邊入隊(duì)一個(gè)元素,僅隊(duì)列存在時(shí)有效 | O(1) |
三老速、使用場景
3.1 消息隊(duì)列
列表類型可以使用 rpush 實(shí)現(xiàn)先進(jìn)先出的功能粥喜,同時(shí)又可以使用 lpop 輕松的彈出(查詢并刪除)第一個(gè)元素,所以列表類型可以用來實(shí)現(xiàn)消息隊(duì)列
3.2 文章(商品等)列表
我們以博客站點(diǎn)為例橘券,當(dāng)用戶和文章都越來越多時(shí)额湘,為了加快程序的響應(yīng)速度,我們可以把用戶自己的文章存入到 List 中旁舰,因?yàn)?List 是有序的結(jié)構(gòu)锋华,所以這樣又可以完美的實(shí)現(xiàn)分頁功能,從而加速了程序的響應(yīng)速度箭窜。
- 每篇文章我們使用哈希結(jié)構(gòu)存儲毯焕,例如每篇文章有3個(gè)屬性title、timestamp磺樱、content纳猫。
hmset acticle:1 title xx timestamp 1476536196 content xxxx
...
hmset acticle:k title yy timestamp 1476512536 content yyyy
...
- 向用戶文章列表添加文章,user:{id}:articles作為用戶文章列表的鍵竹捉。
lpush user:1:acticles article:1 article3
...
lpush
...
- 分頁獲取用戶文章列表续担,例如下面?zhèn)未a獲取用戶id=1的前10篇文章。
articles = lrange user:1:articles 0 9
for article in {articles}
{
hgetall {article}
}
注意:使用列表類型保存和獲取文章列表會存在兩個(gè)問題活孩。
- 如果每次分頁獲取的文章個(gè)數(shù)較多,需要執(zhí)行多次hgetall操作乖仇,此時(shí)可以考慮使用Pipeline批量獲取憾儒,或者考慮將文章數(shù)據(jù)序列化為字符串類型,使用mget批量獲取乃沙。
- 分頁獲取文章列表時(shí)起趾,lrange命令在列表兩端性能較好,但是如果列表較大警儒,獲取列表中間范圍的元素性能會變差训裆,此時(shí)可以考慮將列表做二級拆分眶根,或者使用Redis3.2的quicklist內(nèi)部編碼實(shí)現(xiàn),它結(jié)合ziplist和linkedlist的特點(diǎn)边琉,獲取列表中間范圍的元素時(shí)也可以高效完成属百。
關(guān)于列表的使用場景可參考以下幾個(gè)命令組合:
- lpush+lpop=Stack(棧)
- lpush+rpop=Queue(隊(duì)列)
- lpush+ltrim=Capped Collection(有限集合)
- lpush+brpop=Message Queue(消息隊(duì)列)