Redis基礎(chǔ)學(xué)習(xí):List相關(guān)操作在 Redis 中,List 也是非常常用的一個(gè)數(shù)據(jù)類型,它可以看做是我們 PHP 中的數(shù)字下標(biāo)類型的數(shù)組,注意,是數(shù)字下標(biāo)的那種最典型的數(shù)組格式。重要的是,它可以方便地幫助我們實(shí)現(xiàn)隊(duì)列或者棧的功能,非常強(qiáng)大。同樣的,我們還是先來學(xué)習(xí)一下它的一些基本操作命令。 添加元素添加元素就是兩個(gè)命令,RPUSH 和 LPUSH ,分別從 List 的右邊或者左邊添加數(shù)據(jù)。
上面的例子中,LPUSH 從左邊添加,1最先被添加進(jìn)去,然后是 2 ,因此,返回的順序是 3 2 1 這樣的。同理,RPUSH 的時(shí)候,我們是從右邊添加,-3 -2 -1 從右邊依次進(jìn)入的順序就是我們寫的 -3 -2 -1 這個(gè)順序。這兩個(gè)命令操作成功后都會(huì)返回 List 的長度,如果指定的 key 不存在的話,就創(chuàng)建新的 List 。 LLEN 可以獲取 List 的長度。LRANGE 返回 List 中的數(shù)據(jù),它有兩個(gè)參數(shù),分別是開始位置和結(jié)束位置,其實(shí)我們可以使用 LRANGE a 0 -1 來獲取 List 中的全部元素。注意,-1 表示的反向取數(shù)據(jù),你可以把這種下標(biāo)看成是一種 環(huán)形 取數(shù)據(jù)的方式。
RPUSHX 和 LPUSHX 的意思是如果指定的 key 不存在,則無法添加數(shù)據(jù)。 修改指定下標(biāo)元素能設(shè)置,當(dāng)然也能修改啦,修改使用的是 LSET 命令,它是根據(jù)指定的下標(biāo)進(jìn)行數(shù)據(jù)修改的。
如果指定的下標(biāo)不存在或者是指定的 key 不存在,就無法修改,并且會(huì)分別報(bào)出不同的錯(cuò)誤。 根據(jù)指定元素值插入插入數(shù)據(jù)是根據(jù)某個(gè)值的內(nèi)容進(jìn)行插入,注意,它不是根據(jù)下標(biāo)哦。
從上面的例子可以看出,LINSERT 命令有一個(gè)參數(shù)可以設(shè)置為 BEFORE 或者 AFTER ,分別代表根據(jù)指定值,從它的前面或者后面插入數(shù)據(jù)。
插入成功后,會(huì)返回 List 的長度,如果列表 key 不存在,則不會(huì)有任何操作發(fā)生,如果指定的插入數(shù)據(jù)不存在,則會(huì)返回 -1 ,如果 key 存在,但它不是一個(gè) List 類型的話,那么會(huì)報(bào)錯(cuò)。 獲取元素添加、修改、插入元素的操作非常簡(jiǎn)單,但其實(shí)我們最常用的就是添加那兩個(gè),也就是 LPUSH 和 RPUSH 。同樣的,在獲取元素中,我們最常用的也僅是那么幾個(gè)命令,不過既然是基礎(chǔ)的學(xué)習(xí),我們還是一個(gè)一個(gè)的都過一遍。 獲取指定下標(biāo)的元素通過 LINDEX 命令,就可以獲取到指定下標(biāo)的元素?cái)?shù)據(jù)。
和之前說過的一樣,這里的下標(biāo)也是可以使用負(fù)數(shù)反向獲取的。 根據(jù)值獲取下標(biāo)除了通過下標(biāo)獲取元素之外,還可以通過 LPOS 獲取到指定元素的下標(biāo)。
很顯然,在默認(rèn)情況下,它返回的是第一個(gè)元素的下標(biāo),因?yàn)樵谏厦娴牟僮髦校覀円呀?jīng)有多個(gè) -33 這條數(shù)據(jù)了。那么怎么獲取到其它的 -33 的下標(biāo)呢?
首先,我們多添加一些數(shù)據(jù),現(xiàn)在整個(gè) List 中有四個(gè) -33 元素。
RANK 參數(shù),表示我們現(xiàn)在要獲取第幾個(gè)指定的值的下標(biāo)。例子中的正數(shù) 2 就是正數(shù)第二個(gè) -33 的下標(biāo),也就是 7 ,-2 表示倒數(shù)第二個(gè) -33 的下標(biāo)值,也就是下標(biāo)為 11 的數(shù)據(jù)。除了指定顯示第幾個(gè)之外,我們還可以指定返回多少個(gè),使用 COUNT 參數(shù)。
默認(rèn)情況下,COUNT 參數(shù)就是 1,也就是返回 1 條數(shù)據(jù)。如果我們指定了,就返回符合指定數(shù)據(jù)的查找數(shù)據(jù)。如果設(shè)置為 0 的話,就表示返回全部符合的數(shù)據(jù)下標(biāo)。當(dāng)然,RANK 和 COUNT 也可以組合使用。
這個(gè)例子中,返回的是除了第一個(gè)之外的全部 -33 的下標(biāo)。
如果查找的數(shù)據(jù)不存在,會(huì)返回 nil ,不過有個(gè)特殊情況,那就是如果使用了 COUNT 返回的話,它本身就會(huì)將結(jié)果變成數(shù)組返回,所以如果在這種情況如果沒有找到數(shù)據(jù),就會(huì)返回一個(gè)空數(shù)組。 彈出彈出操作是實(shí)現(xiàn)?;蛘哧?duì)列的重要命令,也是大家會(huì)經(jīng)常使用的兩個(gè)命令,分別是 RPOP 和 LPOP ,很明顯,就是分別從右、左彈出數(shù)據(jù)。彈出的意思就是返回這條數(shù)據(jù)并且在 List 中刪除它。
如果指定 key 不存在,那么這兩個(gè)命令返回的就是 nil 空。 有了這兩個(gè)命令,實(shí)現(xiàn)隊(duì)列和棧就非常簡(jiǎn)單了吧。隊(duì)列的話,如果是 RPUSH ,那就 LPOP ,反過來也行。棧就更簡(jiǎn)單了 RPUSH 配合 RPOP 就好啦! 數(shù)據(jù)移動(dòng)數(shù)據(jù)移動(dòng)在 6.2 之前的版本是使用 RPOPPUSH ,但在 6.2 之后這個(gè)命令就被標(biāo)為過時(shí)了,并且新出了一個(gè) LMOVE 命令。我們就以最新的來進(jìn)行學(xué)習(xí)吧。
LMOVE 的命令調(diào)用方法看著有點(diǎn)蒙啊,其實(shí)很好理解。source 表示從哪里移動(dòng),destination表示移動(dòng)到的目標(biāo),也就是移動(dòng)到哪里去。后面兩個(gè)參數(shù)分別代表是LPOP/RPOP source 和 LPUSH/RPUSH destination 。
我們先從之前的 a 中 LPOP 兩條數(shù)據(jù) LPUSH 到了 a1 中,然后再從 a 中 LPOP 一條數(shù)據(jù) RPUSH 到 a1 中,最后,再從 a 中 RPOP 一條數(shù)據(jù) LPUSH 到了 a1 中。 原子阻塞彈出這是啥意思?我們使用 BLPOP 或者 BRPOP ,當(dāng)隊(duì)列空的時(shí)候,它會(huì)阻塞到這里,直到獲取到下一條數(shù)據(jù)。
我們先彈出所有的數(shù)據(jù),注意,這個(gè) BLPOP 是可以同時(shí)彈出多個(gè)隊(duì)列的,我們?cè)谶@個(gè)例子中,同時(shí)把 a 和 a1 的都彈完,當(dāng)彈出的時(shí)候,返回的是一個(gè)列表,第一個(gè)值是列表的名稱,第二個(gè)是彈出的值。兩個(gè)隊(duì)列中都沒有數(shù)據(jù)后,BLPOP 就阻塞在這里了。最后一個(gè)參數(shù)的 0 表示的是超時(shí)時(shí)間,如果過了超時(shí)時(shí)間還沒有新的元素插入到這兩個(gè) List 中,就會(huì)結(jié)束阻塞,而如果設(shè)置為 0 的話,就會(huì)一直等待。
接著另開一個(gè) redis-cli 客戶端,然后為 a1 添加一條數(shù)據(jù)。
之前的那個(gè)客戶端中阻塞的隊(duì)列馬上響應(yīng)并彈出了一條數(shù)據(jù),并且下面還標(biāo)明了本次等待的時(shí)間。 這個(gè)東西有什么用呢?在某些場(chǎng)景中,比如我們后臺(tái)消費(fèi)隊(duì)列的場(chǎng)景一般就會(huì)是一個(gè)進(jìn)程不停地執(zhí)行消費(fèi),通常我們可能會(huì)寫一個(gè)死循環(huán),然后每次循環(huán)的時(shí)候查找隊(duì)列中是否有內(nèi)容,寫個(gè) LPOP 如果返回空,就 sleep() 一會(huì),如果下次還沒查到,就再 sleep() 一會(huì)。現(xiàn)在,我們可以替換成使用 BLPOP ,其實(shí)就是省去了 sleep() 的過程,并且也可以減少反復(fù)遠(yuǎn)程查詢的次數(shù)。 有 POP 操作,對(duì)應(yīng)的也有阻塞的 BLMOVE 操作,這個(gè)命令也是 6.2 之后才有的,替換之前的 BRPOPPUSH 命令。概念也是和 LMOVE 類似的,所以大家直接看下代碼就好了。
首先我們讓 a 的數(shù)據(jù)全部彈出,現(xiàn)在 BLMOVE 也是在阻塞狀態(tài)。
接著還是通過另一個(gè)客戶端插入數(shù)據(jù),注意,我們給 a1 插入數(shù)據(jù)的時(shí)候不會(huì)有動(dòng)靜,因?yàn)槲覀冃枰o source 插入數(shù)據(jù)才會(huì)發(fā)生數(shù)據(jù)移動(dòng)。
好了,a 新插入的數(shù)據(jù)馬上被移動(dòng)到了 a1 中。 刪除元素其實(shí)上面 POP 相關(guān)的操作也是一種刪除操作,但它們都是定死的只能從某一個(gè)方向去把數(shù)據(jù)彈出來。真正的刪除肯定是我們?nèi)ルS便刪除指定的數(shù)據(jù)嘛,先來看看 LREM 這個(gè)命令。 LREM 是根據(jù)提供的值,去刪除元素。注意,不是下標(biāo),不是下標(biāo),不是下標(biāo)。
第一次,我們刪除了 4 這個(gè)元素,后面的 1 表示只刪除 1 個(gè) 4 。第二次刪除了 2 個(gè) 2 。這個(gè)例子不太好,因?yàn)槲业臄?shù)據(jù)也是數(shù)字,但是,大家要是能一眼看明白這段代碼的意思,就能很清楚地明白 LREM 是刪除值而不是下標(biāo)的意思了。 只保留指定區(qū)間元素除了刪除指定的值之外,我們還可以按范圍刪除,或者說是按范圍保留。
LTRIM key 2 5 的意思就是只保留下標(biāo) 2 到 5 的數(shù)據(jù),或者反過來說是把 0、1 和 5 之后的數(shù)據(jù)都刪除了。 刪除指定下標(biāo)元素Redis 中沒有提供按指定下標(biāo)刪除 List 數(shù)據(jù)的命令,那要實(shí)現(xiàn)刪除指定下標(biāo)的元素要怎么弄呢?
使用 LSET 和 LREM 配合起來,先將要?jiǎng)h除的下標(biāo)位置的元素改成一個(gè)固定的值,比如我這里用的 -1 ,或者你也可以用 del 、invalid 之類的字符串。然后使用 LREM 去批量刪除它們就好了。 總結(jié)奇怪的小知識(shí)又增加了吧?說實(shí)話,之前我還真不知道 LMOVE 和 BLPOP 這一類的東西,既然知道了,那么下回做隊(duì)列的時(shí)候咱也可以試試了。List 相關(guān)的操作命令就是這些,其實(shí)也不難,你可以把它當(dāng)成是一個(gè)可以方便地實(shí)現(xiàn)隊(duì)列和棧的數(shù)組,其實(shí)之前我也一直就只是把它當(dāng)普通隊(duì)列來用的,更復(fù)雜的優(yōu)先和延時(shí)隊(duì)列在 Redis 中還有別的數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn),后面我們也會(huì)學(xué)習(xí)到。 |
|