redis系列:經過隊列案例學習list命令

摘要: 前言 這一篇文章將講述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 的阻塞版本。

RPOPLPUSH和BRPOPLPUSH

這兩個命令做用實際上是相同的,只不過BRPOPLPUSH是阻塞的,當沒有數據時,會一直阻塞,直到有數據。

在Redis官方文檔中,用RPOPLPUSH命令舉了兩個例子,一個是Reliable queue(安全的隊列 ),另外一個是Circular list(循環列表)。

Reliable queue(安全的隊列 )

Redis一般都被用作一個處理各類後臺工做或消息任務的消息服務器。 一個簡單的隊列模式就是:生產者把消息放入一個列表中,等待消息的消費者用 RPOP 命令(用輪詢方式), 或者用 BRPOP 命令(若是客戶端使用阻塞操做會更好)來獲得這個消息。

然而,由於消息有可能會丟失,因此這種隊列並是不安全的。例如,當接收到消息後,出現了網絡問題或者消費者端崩潰了, 那麼這個消息就丟失了。

RPOPLPUSH (或者其阻塞版本的 BRPOPLPUSH) 提供了一種方法來避免這個問題:消費者端取到消息的同時把該消息放入一個正在處理中的列表。 當消息被處理了以後,該命令會使用 LREM 命令來移除正在處理中列表中的對應消息。

另外,能夠添加一個客戶端來監控這個正在處理中列表,若是有某些消息已經在這個列表中存在很長時間了(即超過必定的處理時限), 那麼這個客戶端會把這些超時消息從新加入到隊列中。

翻譯來自 http://www.redis.cn/commands/rpoplpush.html

Circular list(循環列表)

RPOPLPUSH 命令的 source 和 destination 是相同的話, 那麼客戶端在訪問一個擁有n個元素的列表時,能夠在 O(N) 時間裏一個接一個獲取列表元素, 而不用像 LRANGE那樣須要把整個列表從服務器端傳送到客戶端。

上面這種模式即便在如下兩種狀況下照樣能很好地工做: 有多個客戶端同時對同一個列表進行旋轉(rotating):它們會取得不一樣的元素,直到列表裏全部元素都被訪問過,又從頭開始這個操做。 有其餘客戶端在往列表末端加入新的元素。

這個模式讓咱們能夠很容易地實現這樣一個系統:有 N 個客戶端,須要接二連三地對一批元素進行處理,並且處理的過程必須儘量地快。 一個典型的例子就是服務器上的監控程序:它們須要在儘量短的時間內,並行地檢查一批網站,確保它們的可訪問性。

值得注意的是,使用這個模式的客戶端是易於擴展(scalable)且安全的(reliable),由於即便客戶端把接收到的消息丟失了, 這個消息依然存在於隊列中,等下次迭代到它的時候,由其餘客戶端進行處理。

翻譯來自 http://www.redis.cn/commands/rpoplpush.html

案例-約瑟夫問題

約瑟夫問題(有時也稱爲約瑟夫斯置換),是一個出如今計算機科學數學中的問題。在計算機編程的算法中,相似問題又稱爲約瑟夫環

人們站在一個等待被處決的圈子裏。 計數從圓圈中的指定點開始,並沿指定方向圍繞圓圈進行。 在跳過指定數量的人以後,執行下一我的。 對剩下的人重複該過程,從下一我的開始,朝同一方向跳過相同數量的人,直到只剩下一我的,並被釋放。

問題即,給定人數、起點、方向和要跳過的數字,選擇初始圓圈中的位置以免被處決。

來自維基百科 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++;
        }
    }
}

整個代碼步驟以下

  1. 先是模擬有41我的(向redis中key爲josephus的list添加41個數據)
  2. 定義索引index
  3. 循環判斷key爲josephus的數據長度是否大於0
  4. 當索引index爲3時,調用Redis的rpop命令彈出對應的數據。索引index不爲3時,調用RPOPLPUSH命令,將對應的數據放到隊列頭部
  5. 索引index加1

運行結果有點長,這裏只截圖最後一部分的結果,以下

約瑟夫問題代碼請點擊JosephusProblem.java

建議學習的人最好每一個命令都去敲下,加深印象。下面詩句送給大家。

紙上得來終覺淺,絕知此事要躬行。————出自《冬夜讀書示子聿》

原文連接

相關文章
相關標籤/搜索