redis系列:通过队列案例学习list命令
前言
這一篇文章將講述Redis中的list類型命令,同樣也是通過demo來講述,其他部分這里就不在贅述了。
項目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-list
案例
demo功能是隊列,整個demo的大致頁面如下。左邊是存儲到Redis中的數(shù)據(jù),右邊是從Redis中彈出的數(shù)據(jù)。
準備工作
首先定義一個存儲list的key
private static final String LIST_KEY = "list:1";隊列的key就用list:1
redis操作對象
private RedisTemplate redisTemplate; //string 命令操作對象 private ValueOperations valueOperations; //list 命令操作對象 private ListOperations listOperations;list在Redis中的結(jié)構(gòu)可以看下圖(圖片來源于Redis in Action)。
插入數(shù)據(jù)
頭部插入
命令介紹
| LPUSH | LPUSH key value [value ...] | 將所有指定的值插入到存于 key 的列表的頭部。 如果 key 不存在,那么在進行 push 操作前會創(chuàng)建一個空列表。 |
| LPUSHX | LPUSHX key value | 只有當 key 已經(jīng)存在并且存著一個 list 的時候,在這個 key 下面的 list 的頭部插入 value。 |
接下來看看demo中頭部插入的功能,點擊下圖中頭部插入按鈕,然后在彈出框中填入數(shù)字0,點擊提交后整個頭部插入流程結(jié)束。可以看到左邊的隊列數(shù)據(jù)出現(xiàn)了一條{"data":"0"} 數(shù)據(jù),在數(shù)據(jù){"data":"1"} 上面。
來看看后臺的方法
@RequestMapping(value = "/leftPop",method = RequestMethod.GET) public Object leftPop(){return listOperations.leftPop(LIST_KEY); }如果需要在Redis中操作,可以敲下面的命令
lpush list:1 "{\"data\":\"0\"}"尾部插入
命令介紹
| RPUSH | RPUSH key value [value ...] | 向存于 key 的列表的尾部插入所有指定的值。如果 key 不存在,那么會創(chuàng)建一個空的列表然后再進行 push 操作。 |
| RPUSHX | RPUSHX key value | 將值 value 插入到列表 key 的表尾, 當且僅當 key 存在并且是一個列表。 |
接下來看看demo中尾部插入的功能,點擊下圖中尾部插入按鈕,然后在彈出框中填入數(shù)字11,點擊提交后整個新增流程結(jié)束。可以看到左邊的隊列數(shù)據(jù)出現(xiàn)了一條{"data":"11"} 數(shù)據(jù),在數(shù)據(jù){"data":"10"}下面。
來看看后臺的方法
@RequestMapping(value = "/rightPop",method = RequestMethod.GET) public Object rightPop(){return listOperations.rightPop(LIST_KEY); }如果需要在Redis中操作,可以敲下面的命令
rpush list:1 "{\"data\":\"11\"}"列表查詢
命令介紹
同樣先看看相關(guān)的獲取值命令
| LRANGE | LRANGE key start stop | 返回存儲在 key 的列表里指定范圍內(nèi)的元素。 |
| LINDEX | LINDEX key index | 返回列表里的元素的索引 index 存儲在 key 里面。 |
| LLEN | LLEN key | 返回存儲在 key 里的list的長度。 |
后臺查詢方法,將新增的內(nèi)容查詢出來
@RequestMapping(value = "/getList",method = RequestMethod.GET) public List getList(){List list = listOperations.range(LIST_KEY, 0, -1);//可以用size獲取成員長度//listOperations.size(LIST_KEY);return list; }數(shù)據(jù)彈出
頭部彈出
| LPOP | LPOP key | 移除并且返回 key 對應(yīng)的 list 的第一個元素。 |
| BLPOP | BLPOP key [key ...] timeout | 它是命令?LPOP?的阻塞版本,這是因為當給定列表內(nèi)沒有任何元素可供彈出的時候, 連接將被?BLPOP?命令阻塞。 |
接下來看看頭部彈出的功能,點擊下圖中頭部彈出按鈕,可以看到左邊的隊列頂部數(shù)據(jù)減少了,在右邊彈出的數(shù)據(jù)出現(xiàn)了左邊隊列數(shù)據(jù)消失的數(shù)據(jù)。
來看看后臺的方法
@RequestMapping(value = "/leftPop",method = RequestMethod.GET) public Object leftPop(){return listOperations.leftPop(LIST_KEY); }如果需要在Redis中操作,可以敲下面的命令
lpop list:1尾部彈出
| RPOP | RPOP key | 移除并返回存于 key 的 list 的最后一個元素。 |
| BRPOP | BRPOP key [key ...] timeout | 它是?RPOP?的阻塞版本,因為這個命令會在給定list無法彈出任何元素的時候阻塞連接。 |
接下來看看尾部彈出的功能,點擊下圖中尾部彈出按鈕,可以看到左邊的隊列尾部數(shù)據(jù)減少了,在右邊彈出的數(shù)據(jù)出現(xiàn)了左邊隊列數(shù)據(jù)消失的數(shù)據(jù)。
來看看后臺的方法
@RequestMapping(value = "/rightPop",method = RequestMethod.GET) public Object rightPop(){return listOperations.rightPop(LIST_KEY); }如果需要在Redis中操作,可以敲下面的命令
rpop list:1其他命令
| LINSERT | LINSERT key BEFORE\ | AFTER pivot value | 把 value 插入存于 key 的列表中在基準值 pivot 的前面或后面。 |
| LREM | LREM key count value | 從存于 key 的列表里移除前 count 次出現(xiàn)的值為 value 的元素。 | |
| LSET | LSET key index value | 設(shè)置 index 位置的list元素的值為 value。 | |
| LTRIM | LTRIM key start stop | 修剪(trim)一個已存在的 list,這樣 list 就會只包含指定范圍的指定元素。 | |
| RPOPLPUSH | RPOPLPUSH source destination | 原子性地返回并移除存儲在 source 的列表的最后一個元素(列表尾部元素), 并把該元素放入存儲在 destination 的列表的第一個元素位置(列表頭部)。 | |
| BRPOPLPUSH | BRPOPLPUSH source destination timeout | BRPOPLPUSH?是?RPOPLPUSH?的阻塞版本。 |
RPOPLPUSH和BRPOPLPUSH
這兩個命令作用其實是相同的,只不過BRPOPLPUSH是阻塞的,當沒有數(shù)據(jù)時,會一直阻塞,直到有數(shù)據(jù)。
在Redis官方文檔中,用RPOPLPUSH命令舉了兩個例子,一個是Reliable queue(安全的隊列 ),另一個是Circular list(循環(huán)列表)。
Reliable queue(安全的隊列 )
Redis通常都被用做一個處理各種后臺工作或消息任務(wù)的消息服務(wù)器。 一個簡單的隊列模式就是:生產(chǎn)者把消息放入一個列表中,等待消息的消費者用?RPOP?命令(用輪詢方式), 或者用 BRPOP 命令(如果客戶端使用阻塞操作會更好)來得到這個消息。
然而,因為消息有可能會丟失,所以這種隊列并是不安全的。例如,當接收到消息后,出現(xiàn)了網(wǎng)絡(luò)問題或者消費者端崩潰了, 那么這個消息就丟失了。
RPOPLPUSH (或者其阻塞版本的?BRPOPLPUSH) 提供了一種方法來避免這個問題:消費者端取到消息的同時把該消息放入一個正在處理中的列表。 當消息被處理了之后,該命令會使用 LREM 命令來移除正在處理中列表中的對應(yīng)消息。
另外,可以添加一個客戶端來監(jiān)控這個正在處理中列表,如果有某些消息已經(jīng)在這個列表中存在很長時間了(即超過一定的處理時限), 那么這個客戶端會把這些超時消息重新加入到隊列中。
翻譯來自?http://www.redis.cn/commands/rpoplpush.html
Circular list(循環(huán)列表)
RPOPLPUSH 命令的 source 和 destination 是相同的話, 那么客戶端在訪問一個擁有n個元素的列表時,可以在 O(N) 時間里一個接一個獲取列表元素, 而不用像?LRANGE那樣需要把整個列表從服務(wù)器端傳送到客戶端。
上面這種模式即使在以下兩種情況下照樣能很好地工作:?有多個客戶端同時對同一個列表進行旋轉(zhuǎn)(rotating):它們會取得不同的元素,直到列表里所有元素都被訪問過,又從頭開始這個操作。?有其他客戶端在往列表末端加入新的元素。
這個模式讓我們可以很容易地實現(xiàn)這樣一個系統(tǒng):有 N 個客戶端,需要連續(xù)不斷地對一批元素進行處理,而且處理的過程必須盡可能地快。 一個典型的例子就是服務(wù)器上的監(jiān)控程序:它們需要在盡可能短的時間內(nèi),并行地檢查一批網(wǎng)站,確保它們的可訪問性。
值得注意的是,使用這個模式的客戶端是易于擴展(scalable)且安全的(reliable),因為即使客戶端把接收到的消息丟失了, 這個消息依然存在于隊列中,等下次迭代到它的時候,由其他客戶端進行處理。
翻譯來自?http://www.redis.cn/commands/rpoplpush.html
案例-約瑟夫問題
約瑟夫問題(有時也稱為約瑟夫斯置換),是一個出現(xiàn)在計算機科學和數(shù)學中的問題。在計算機編程的算法中,類似問題又稱為約瑟夫環(huán)。
人們站在一個等待被處決的圈子里。 計數(shù)從圓圈中的指定點開始,并沿指定方向圍繞圓圈進行。 在跳過指定數(shù)量的人之后,執(zhí)行下一個人。 對剩下的人重復該過程,從下一個人開始,朝同一方向跳過相同數(shù)量的人,直到只剩下一個人,并被釋放。
問題即,給定人數(shù)、起點、方向和要跳過的數(shù)字,選擇初始圓圈中的位置以避免被處決。
來自維基百科?https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98
思路
定義一個list key為josephus,利用
RPOPLPUSH josephus josephus命令來構(gòu)造循環(huán)鏈表,每當數(shù)到3時,使用rpop
rpop josephus命令彈出
代碼實現(xiàn)
public class JosephusProblem extends RedisBaseConnection {@Testpublic void test() {//構(gòu)造數(shù)據(jù)for (int i = 1; i <= 41; i++) {listOperations.leftPush("josephus", String.valueOf(i));}int index = 1;while (listOperations.size("josephus") > 0) {//當數(shù)到3時,彈出if (index == 3) {System.out.println(listOperations.range("josephus", 0, -1));System.out.println("當前被殺的人是:" + listOperations.rightPop("josephus"));index = 0;} else {listOperations.rightPopAndLeftPush("josephus", "josephus");}index++;}} }整個代碼步驟如下
運行結(jié)果有點長,這里只截圖最后一部分的結(jié)果,如下
約瑟夫問題代碼請點擊JosephusProblem.java
建議學習的人最好每個命令都去敲下,加深印象。下面詩句送給你們。
紙上得來終覺淺,絕知此事要躬行。————出自《冬夜讀書示子聿》
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的redis系列:通过队列案例学习list命令的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 带您探究云存储的奥秘,三分钟帮您快速了解
- 下一篇: 关于机器学习,你需要知道的三件事!