摘要: 前言 這一篇文章將講述Redis中的list類型命令,一樣也是經過demo來說述,其餘部分這裏就不在贅述了。 項目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-list 案例 demo功能是隊列,整個demo的大體頁面以下。html
這一篇文章將講述Redis中的list類型命令,一樣也是經過demo來說述,其餘部分這裏就不在贅述了。java
項目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-listgit
demo功能是隊列,整個demo的大體頁面以下。左邊是存儲到Redis中的數據,右邊是從Redis中彈出的數據。github
首先定義一個存儲list的keyredis
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中的結構能夠看下圖(圖片來源於Redis in Action)。安全
命令介紹服務器
命令 | 用例 | 描述 |
---|---|---|
LPUSH | LPUSH key value [value ...] | 將全部指定的值插入到存於 key 的列表的頭部。 若是 key 不存在,那麼在進行 push 操做前會建立一個空列表。 |
LPUSHX | LPUSHX key value | 只有當 key 已經存在而且存着一個 list 的時候,在這個 key 下面的 list 的頭部插入 value。 |
接下來看看demo中頭部插入的功能,點擊下圖中頭部插入按鈕,而後在彈出框中填入數字0,點擊提交後整個頭部插入流程結束。能夠看到左邊的隊列數據出現了一條{"data":"0"} 數據,在數據{"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 不存在,那麼會建立一個空的列表而後再進行 push 操做。 |
RPUSHX | RPUSHX key value | 將值 value 插入到列表 key 的表尾, 當且僅當 key 存在而且是一個列表。 |
接下來看看demo中尾部插入的功能,點擊下圖中尾部插入按鈕,而後在彈出框中填入數字11,點擊提交後整個新增流程結束。能夠看到左邊的隊列數據出現了一條{"data":"11"} 數據,在數據{"data":"10"}下面。
來看看後臺的方法
@RequestMapping(value = "/rightPop",method = RequestMethod.GET) public Object rightPop(){ return listOperations.rightPop(LIST_KEY); }
若是須要在Redis中操做,能夠敲下面的命令
rpush list:1 "{\"data\":\"11\"}"
一樣先看看相關的獲取值命令
命令 | 用例 | 描述 |
---|---|---|
LRANGE | LRANGE key start stop | 返回存儲在 key 的列表裏指定範圍內的元素。 |
LINDEX | LINDEX key index | 返回列表裏的元素的索引 index 存儲在 key 裏面。 |
LLEN | LLEN key | 返回存儲在 key 裏的list的長度。 |
後臺查詢方法,將新增的內容查詢出來
@RequestMapping(value = "/getList",method = RequestMethod.GET) public List getList(){ List list = listOperations.range(LIST_KEY, 0, -1); //能夠用size獲取成員長度 //listOperations.size(LIST_KEY); return list; }
命令 | 用例 | 描述 |
---|---|---|
LPOP | LPOP key | 移除而且返回 key 對應的 list 的第一個元素。 |
BLPOP | BLPOP key [key ...] timeout | 它是命令 LPOP 的阻塞版本,這是由於當給定列表內沒有任何元素可供彈出的時候, 鏈接將被 BLPOP 命令阻塞。 |
接下來看看頭部彈出的功能,點擊下圖中頭部彈出按鈕,能夠看到左邊的隊列頂部數據減小了,在右邊彈出的數據出現了左邊隊列數據消失的數據。
來看看後臺的方法
@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沒法彈出任何元素的時候阻塞鏈接。 |
接下來看看尾部彈出的功能,點擊下圖中尾部彈出按鈕,能夠看到左邊的隊列尾部數據減小了,在右邊彈出的數據出現了左邊隊列數據消失的數據。
來看看後臺的方法
@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 次出現的值爲 value 的元素。 | |
LSET | LSET key index value | 設置 index 位置的list元素的值爲 value。 | |
LTRIM | LTRIM key start stop | 修剪(trim)一個已存在的 list,這樣 list 就會只包含指定範圍的指定元素。 | |
RPOPLPUSH | RPOPLPUSH source destination | 原子性地返回並移除存儲在 source 的列表的最後一個元素(列表尾部元素), 並把該元素放入存儲在 destination 的列表的第一個元素位置(列表頭部)。 | |
BRPOPLPUSH | BRPOPLPUSH source destination timeout | BRPOPLPUSH 是 RPOPLPUSH 的阻塞版本。 |
這兩個命令做用實際上是相同的,只不過BRPOPLPUSH是阻塞的,當沒有數據時,會一直阻塞,直到有數據。
在Redis官方文檔中,用RPOPLPUSH命令舉了兩個例子,一個是Reliable queue(安全的隊列 ),另外一個是Circular list(循環列表)。
Redis一般都被用作一個處理各類後臺工做或消息任務的消息服務器。 一個簡單的隊列模式就是:生產者把消息放入一個列表中,等待消息的消費者用 RPOP 命令(用輪詢方式), 或者用 BRPOP 命令(若是客戶端使用阻塞操做會更好)來獲得這個消息。
然而,由於消息有可能會丟失,因此這種隊列並是不安全的。例如,當接收到消息後,出現了網絡問題或者消費者端崩潰了, 那麼這個消息就丟失了。
RPOPLPUSH (或者其阻塞版本的 BRPOPLPUSH) 提供了一種方法來避免這個問題:消費者端取到消息的同時把該消息放入一個正在處理中的列表。 當消息被處理了以後,該命令會使用 LREM 命令來移除正在處理中列表中的對應消息。
另外,能夠添加一個客戶端來監控這個正在處理中列表,若是有某些消息已經在這個列表中存在很長時間了(即超過必定的處理時限), 那麼這個客戶端會把這些超時消息從新加入到隊列中。
RPOPLPUSH 命令的 source 和 destination 是相同的話, 那麼客戶端在訪問一個擁有n個元素的列表時,能夠在 O(N) 時間裏一個接一個獲取列表元素, 而不用像 LRANGE那樣須要把整個列表從服務器端傳送到客戶端。
上面這種模式即便在如下兩種狀況下照樣能很好地工做: 有多個客戶端同時對同一個列表進行旋轉(rotating):它們會取得不一樣的元素,直到列表裏全部元素都被訪問過,又從頭開始這個操做。 有其餘客戶端在往列表末端加入新的元素。
這個模式讓咱們能夠很容易地實現這樣一個系統:有 N 個客戶端,須要接二連三地對一批元素進行處理,並且處理的過程必須儘量地快。 一個典型的例子就是服務器上的監控程序:它們須要在儘量短的時間內,並行地檢查一批網站,確保它們的可訪問性。
值得注意的是,使用這個模式的客戶端是易於擴展(scalable)且安全的(reliable),由於即便客戶端把接收到的消息丟失了, 這個消息依然存在於隊列中,等下次迭代到它的時候,由其餘客戶端進行處理。
約瑟夫問題(有時也稱爲約瑟夫斯置換),是一個出如今計算機科學和數學中的問題。在計算機編程的算法中,相似問題又稱爲約瑟夫環。
人們站在一個等待被處決的圈子裏。 計數從圓圈中的指定點開始,並沿指定方向圍繞圓圈進行。 在跳過指定數量的人以後,執行下一我的。 對剩下的人重複該過程,從下一我的開始,朝同一方向跳過相同數量的人,直到只剩下一我的,並被釋放。
問題即,給定人數、起點、方向和要跳過的數字,選擇初始圓圈中的位置以免被處決。
來自維基百科 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
命令來構造循環鏈表,每當數到3時,使用rpop
rpop josephus
命令彈出
代碼實現
public class JosephusProblem extends RedisBaseConnection { @Test public void test() { //構造數據 for (int i = 1; i <= 41; i++) { listOperations.leftPush("josephus", String.valueOf(i)); } int index = 1; while (listOperations.size("josephus") > 0) { //當數到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++; } } }
整個代碼步驟以下
運行結果有點長,這裏只截圖最後一部分的結果,以下
約瑟夫問題代碼請點擊JosephusProblem.java
建議學習的人最好每一個命令都去敲下,加深印象。下面詩句送給大家。
紙上得來終覺淺,絕知此事要躬行。————出自《冬夜讀書示子聿》