標籤:
一、事務
  Redis中的事務是一組命令的集合。一個事務中的命令要麼都執行,要麼都不執行。
  1、事務簡介
  事務的原理是先將一個事務的命令發送給Redis,然後再讓Redis依次執行這些命令。下面看一個樣本:
  
  首先,使用multi命令告訴Redis:下面我給你的命令屬於同一個事務,你先不要執行,而是暫時存起來。
  然後,我們發送兩個set命令來實現賦值,可以看到redis沒有執行這些命令,而是返回queued表示這兩條命令已經進入等待執行的事務隊列中。
  當所有要在同一事務中執行的命令都發給Redis後,用exec命令告訴Redis將等待執行的事務隊列中所有的命令按照發送順序依次執行。
  2、錯誤處理
  當一個事務中某個命令執行出錯時,Redis會怎樣處理呢?這裡有三種情況:
  情況一:語法錯誤,指錯命令不存在或者命令參數的個數不對
  
  可以看到,跟在multi命令後執行了三個命令,第二個和第三個命令有語法錯誤,執行exec後redis就會執行返回錯誤。
  情況二:運行錯誤,指在命令執行時出現的錯誤。
  
  這種錯誤在實際執行前redis是無法發現的,所以在事務裡這樣的命令是會被redis接受和執行的,如果事務裡一條命令出現了運行錯誤,事務裡其他的命令依然會繼續執行(包括出錯命令之後的命令)。
  注意:Redis事務沒有關係型資料庫提供的復原功能。
  3、Watch命令
  watch命令可以監控一個或多個鍵,一旦其中一個鍵被修改或刪除,之後的事務就不會執行,監控一直持續到exec命令(但是不能保證其他客戶不修改這一索引值)。
  
  上例中執行watch命令後,事務執行前修改了key值,所以事務中命令set username xujian沒有執行,exec命令返回空結果。
   注意:執行exec命令後立即取消對所有鍵的監控,如果不想執行事務中的命令也可以使用unwatch命令來保證下一個事務的執行不會受到影響。
二、存留時間
  在Redis中,可以使用expire命令設定一個鍵的存留時間,到時間後Redis會自動刪除它。
  expire命令的使用方法為expire key seconds,其中seconds參數表示鍵的存留時間,單位是秒。如果想知道一個鍵還有多久的時間會被刪除,可以使用TTL命令,傳回值是鍵的剩餘時間(單位是秒)。如果想取消鍵的存留時間設定,即將恢複永久的,可以使用persist命令。
  
三、排序  1、有序集合
  有序集合常見的使用情境是大資料排序,如遊戲的玩家熱門排行榜。
  2、sort命令
  sort命令可以對清單類型、集合類型和有序集合類型鍵進行排序,並且可以完成與關聯式資料庫中的串連查詢相類似的任務。
  對List進行排序:
  
  對有序集合類型排序時會忽略元素的分數,只針對元素自身的值進行排序:
  
3、by參數
  by參數的文法為“by 參考鍵”,其中參考鍵可以是字串類型鍵或者是散列類型鍵的某個欄位。如果提供了by參數,sort命令將不再依據元素自身的值進行排序,而是對每個元素使用元素的值替換參考鍵中的第一個“*”並擷取其值,然後依據該值對元素排序:
  
4、store參數
  預設情況下,sort會直接返回排序結果,如果希望儲存排序結果,可以使用store參數。
  注意:sort命令的時間複雜度是O(n+mlogm),其中n表示要排序的列表(集合或有序集合)中的元素個數,m表示要返回的元素的個數。當n較大的時候sort命令的效能相對較低,並且redis在排序前會建立一個長度為n的容器來儲存待排序的元素。在開發中使用sort要注意以下幾點:
  1、儘可能減少待排序鍵中元素的數量(使n儘可能小)
  2、使用limit參數只擷取需要的資料(使m儘可能小)
  3、如果要排序的資料量較大,儘可能使用store參數將結果緩衝
四、訊息通知
  一般來說,訊息佇列有兩種情境,一種是生產者消費者模式,一種是發行者訂閱者模式。利用redis這兩種情境的訊息佇列都能實現。
  1、生產者消費者模式
  生產者生產訊息放到隊列中,多個消費者同時監聽隊列,誰先搶到訊息誰就會從隊列中取走訊息,即對於每個訊息最多隻能被一個消費者擁有。
  具體的方法就是建立一個任務隊列,生產者主動lpush訊息,而消費者去rpop資料。但是這樣存在一個問題,就是消費者需要主動去請求資料,周期性的請求會造成資源的浪費。如果可以實現一旦有新訊息排入佇列就通知消費者就好了,這時藉助brpop命令就可以實現這樣的需求。brpop和rpop命令相似,唯一區別就是當列表中沒有元素時,brpop命令會一直阻塞住串連,直到有新元素加入。
BRPOP key timeout
  brpop命令接收兩個參數,第一個參數key為索引值,第二個參數timeout為逾時時間。BRPOP命令取資料時候,如果暫時不存在資料,該命令會一直阻塞直到達到逾時時間。如果timeout設定為0,那麼就會無限等待下去。
  
  2、發行者訂閱者模式
  發行者生產訊息放到隊列裡,多個監聽隊列的訂閱者都會受到同一份訊息。
  生產者使用下面命令來發布訊息:
PUBLISH CHANNEL MESSAGE
  訂閱者通過下面的命令來訂閱訊息,執行subscribe命令後,用戶端進入訂閱狀態,處於此狀態的用戶端不能使用4個屬於“發布/訂閱”模型的命令之外的命令。另外,可以使用subscribe channel1.1 channel1.2 ... 同時訂閱多個頻道。
SUBSCRIBE CHANNEL
  
  3、Java實現的redis的訊息佇列
  在jedis中,有對應的方法進行訂閱和發布,為了傳輸對象,需要將對象進行序列化,並封裝成字串進行處理。
  下面我們要實現三個類,一個對應publish,一個對應subscribe,一個對應要傳遞的對象實體類:
  實體類:
import java.io.Serializable;/** * 實體類 * 封裝訊息 * @author Administrator * */public class Message implements Serializable{    private static final long serialVersionUID = 1L;    private String title;    private String content;    public String getTitle()    {        return title;    }    public void setTitle(String title)    {        this.title = title;    }    public String getContent()    {        return content;    }    public void setContent(String content)    {        this.content = content;    }}
  Publish類:
import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import redis.clients.jedis.Jedis;/** * 發行者,用於發布訊息 * @author Administrator * */public class TestPub{    public static void main(String[] args)    {        Jedis jedis = new Jedis("127.0.0.1");        try        {            Message message = new Message();            message.setTitle("體育新聞");            message.setContent("著名NBA球星科比退役了!");            ByteArrayOutputStream baos = new ByteArrayOutputStream();            ObjectOutputStream oos = new ObjectOutputStream(baos);            oos.writeObject(message);            String msg1 = baos.toString("ISO-8859-1");            jedis.publish("foo", msg1);        } catch (Exception e)        {            e.printStackTrace();        }        jedis.close();    }}
  Subscribe類:
import java.io.ByteArrayInputStream;import java.io.ObjectInputStream;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPubSub;/** * 訂閱者,用於接收訊息 * @author Administrator * */public class TestSub{    public static void main(String[] args)    {        Jedis jedis = new Jedis("127.0.0.1");        JedisPubSub jedisPubSub = new JedisPubSub()         {            @Override            public void onUnsubscribe(String channel, int subscribedChannels)            {            }            @Override            public void onSubscribe(String channel, int subscribedChannels)            {            }            @Override            public void onPUnsubscribe(String pattern, int subscribedChannels)            {            }            @Override            public void onPSubscribe(String pattern, int subscribedChannels)            {            }            @Override            public void onPMessage(String pattern, String channel, String message)            {            }            @Override            public void onMessage(String channel, String message)            {                try                {                    ByteArrayInputStream bis = new ByteArrayInputStream(message.getBytes("ISO-8859-1"));
            // 此處指定字元集將字串編碼成位元組數組,此處的字元集需要與發布時的字元集保持一致                    ObjectInputStream ois = new ObjectInputStream(bis);                    Message  message2= (Message) ois.readObject();                    System.out.println(message2.getTitle()+"\n"+message2.getContent());                } catch (Exception e)                {                    e.printStackTrace();                } finally                {                }            }        };        jedis.subscribe(jedisPubSub, "foo");        jedis.close();    }}
  先執行訂閱操作,然後發行者發布訊息,執行結果為:
  
五、參考資料
  1、Redis入門指南
Redis學習筆記二