Redis 詳解


1. Redis 概述

  1. Redis:遠程字典服務器(REmote DIctionary Server);
  2. Redis 是一個高性能(key/value)分布式內存數據庫,基於內存運行並支持持久化的NoSQL數據庫;
  3. Redis 三個特點:
    • Redis 支持數據的持久化,可以將內存中的數據保存在磁盤中,重啟的時候,可以再次加載進行使用;
    • Redis 不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲;
    • Redis 支持數據的備份,即master-slave模式的數據備份;

2. Redis 數據類型

2.1 Redis 的五大數據類型

  • String(字符串)
  • Hash(哈希,類似java里的Map)
  • List
  • Set
  • Zset(sorted set: 有序集合)

2.2 Redis 鍵(key)

  • set 鍵名 鍵值: 向數據庫中存儲鍵值對;
  • keys *: 查看當前數據庫所有鍵;
  • exits 鍵名: 判斷某個key是否存在,存在,返回"1";不存在,返回"0";
  • move 鍵名 數據庫(例如2): 將某個鍵剪切至2號數據庫;
  • expire key 秒: 為指定的key設置過期時間;
  • ttl key: 查看該key,還有多少秒過期,-1表示永不過期, -2表示已過期;
  • type key: 查看key的類型;

2.3 Redis 字符串(String)

  • set/get/del 鍵名: 設置/獲取/刪除;
  • append 鍵名(key) 值: 向key所對應的value后面添加值;
  • strlen 鍵名(key): 獲取某個key所對應的value的字符串長度;
  • incr/decr/incrby/decrby: 一定要是數字才能進行加減;
  • getrange k2 0 3: 表示獲取k2所對應的value的前四個字符;
  • setrange: 設置指定區間范圍內的值;
  • setex(set with expire) 秒: 設置過期時間;
  • setnx(set if not exist)
  • mset/mget/msetnx: 設置(獲取)多個, 例,mset k2 v2 k3 v3 k4 v4;
  • getset: 先get再set;

2.4 Redis 列表(List)

  • LPUSH list01 1 2 3 4 5: 存儲list; "LPUSH(Left push)"
  • LRANGE list01: 獲取,"5,4,3,2,1";
  • RPUSH list02 1 2 3 4 5
  • LRANGE list02: 獲取, "1 2 3 4 5"
  • LPOP/RPOP
  • LINDEX: 按照索引下標獲得元素(從上到下);
  • LLEN: 獲取list集合的長度;
  • RUPUSH list03 1 1 1 2 2 2 3 3 3 4 4 4 5 6 7
    LREM list03 2 3: 刪除list03集合中兩個3(LREM, left remove);
  • LTRIM list03 0 4: 截取指定范圍(0~4)的值,然后賦值給list03;
  • RPOPLPUSH 源列表 目的列表
  • LSET list03 1 x: 將list03中的下標為1的鍵的值設置為"x";
  • LINSERT list03 before(after) x java: 在"x"的前面(或后面)插入"java";

2.5 Redis 集合(Set)

  • SADD set01 1 1 2 2 3 3: 向set01中插入值,只會插入"1 2 3",即不允許重復;
  • SMEMBERS set01: 獲取set01集合中的值
  • SISMEMBER set01 1: 判斷"1"是否為集合set01成員; 如果是,返回1;不是,返回0;
  • SCARD: 獲取集合里面元素個數;
  • SREM key member[memeber...]:移除集合key中的一個或多個memeber元素;
  • SRANDMEMEBER set01 3: 從set01集合中隨機取三個值;
  • SPOP set01:從set01集合中,隨機移除一個元素,並返回該元素;
  • SMOVE source destination memeber: 將 memeber 元素從source集合移動到destination集合;
  • SDIFF: 差集;
  • SINTER: 交集;
  • SUNION: 並集;

2.6 Redis 哈希(Hash)

  • 類似於 Map<String,Object>;
  • KV模式不變,但V是一個鍵值對;
  • hset user gender male: 將 user 哈希表中的gender值設為male;
  • hget user gender: 獲取;
  • hmset customer id 11 name zhangsan age 22:同時將多個field-value(域-值)對設置到哈希表customer中
  • hmget customer id name age: 獲取多個值;
  • hgetall customer: 獲取所有的域和值;
  • hdel customer id name: 刪除哈希表中的id和name域;
  • hlen customer:獲取哈希表的長度;
  • hexists customer id: 查看哈希表customer中是否存在id域;
  • hkeys customer: 獲取哈希表customer中的所有域;
  • hvalues customer: 獲取哈希表customer中的所有域的值;
  • hincryby key field increment: 為哈希表key中的域field的值加上增量increment;
  • hincrbyfloat
  • hsetnx key field value: 當域field不存在時,將該域field的值設置為value;

2.7 Redis 有序集合(Zset,sorted set)

  • Zset 在set基礎上,加上一個score值;之前set集合是:k1 v1 v2 v3,現在,Zset是k1 score1 v1 score2 v2
  • zadd key score1 mem1 score2 mem2:將一個或多個memeber元素及其score的值加入到有序集key當中;
  • zrangebyscore key 開始min 結束max: 返回有序集key中,所有score介於min和max之間;
  • zrangebysroce zset (1 5: 返回所有符合條件 1<score<=5 的成員;
  • zrem key memeber: 移除有序集key中的一個或多個成員;
  • zcard key: 返回有序集合key的基數;
  • zcount key min max: 返回有序集key中,score值在min和max之間的成員數量;
  • zrank key member: 返回有序集key中成員memeber的下標;
  • zscore key member: 返回有序集key中,成員memeber對應的score值;
  • zrevrank key member: 逆序獲得成員member下標值;
  • zrevrange key 0 -1: 逆序獲得有序集key的所有的member成員;
  • zrevrangebyscore

3. Redis 的持久化

  • RDB(Redis DataBase)
  • AOF(Append Only File)

3.1 RDB(Redis DataBase)

3.1.1 RDB 概述
  1. RDB: 在指定的時間間隔內,將內存中的數據集快照(snapshot)寫入磁盤,它恢復時,是將快照文件直接讀到內存里;
  2. Redis 會單獨創建(fork)一個子進程來進行持久化,會先將數據寫入到一個臨時文件中,待持久化過程都結束了,
    在用這個臨時文件替換上次持久化好的文件;
  3. 整個過程中,主進程是不進行任何IO操作的,這就確保了極高的性能;
  4. 如果需要進行大規模數據的恢復,且對於數據恢復的完整性不是非常敏感,那RDB方式要比AOF方式更加高效;
  5. RDB 的缺點: 最后一次持久化后的數據可能丟失;
  6. RDB 保存的是 dump.rdb;
  7. RDB 是整個內存壓縮過的Snapshot, RDB持久化策略,與快照觸發條件一樣,默認值
    • 1分鍾內修改了1萬次;
    • 5分鍾內修改了10次;
    • 15分鍾內修改了1次;
3.1.2 Fork
  • Fork的作用: 是復制一個與當前進程一樣的進程;新進程的所有數據(變量,環境變量,程序計數器等)數值都和原進程
    一致,但是是一個全新的進程,並作為原進程的子進程;
3.1.3 觸發RDB快照的方式(三種)
  • 配置文件中默認的快照配置,冷拷貝以后重新使用(即備份文件和要恢復備份的機器不是同一台);
  • savebgsave命令,立刻生成dump.rbd文件;
    • save時,只管保存,其他不管,全部阻塞;
    • bgsave:Redis 會在后台異步執行快照操作,快照的同時還可以響應客戶端請求;可以通過lastsave命令
      獲取最后一次成功執行快照的時間;
  • 執行flushall命令,也會產生dump.rdb文件,但里面是空的,無意義;
3.1.4 恢復備份文件
  • 將備份文件(dump.rdb)移動到redis安裝目錄並啟動服務即可;
3.1.5 RDB優缺點
  • 優勢:
    • 適合大規模的數據恢復;
    • 對數據完整型和一致性要求不高;
  • 劣勢
    • 在一定間隔時間做一次備份,所以如果redis意外down掉的話,就會丟失最后一次快照后的所有修改;
    • Fork 的適合,內存中的數據被克隆了一份,大致2倍的膨脹性需要考慮;

3.2 AOF(Append Only File)

3.2.1 AOF 概述
  1. 以日志的形式來記錄每個寫操作,將Redis執行過的所有寫指令記錄下來(讀操作不記錄),只需追加文件但不可以改寫文
    件, redis啟動之初會讀取該文件,重新構建數據,換言之,redis重啟的話,就根據日志文件的內容將寫指令從前到后執行一
    次以完成數據的恢復工作;
  2. redis.conf中的配置appendonly no,即該種持久化方式默認為關閉;
  3. redis.conf中的配置appendfsync=everysec,出廠默認值,異步操作,每秒記錄,若一秒內宕機,有數據丟失;
    • appendfsync=always: 同步持久化,每次發生數據變更會被立即記錄到磁盤,性能較差但數據完整性比較好;
3.2.2 Rewrite
  1. AOF 采用文件追加方式,文件會越來越大,為避免出現此種情況,新增了重寫機制,當AOF文件的大小超過所設定的閾值時,
    Redis就會啟動AOF文件的內容壓縮,只保留可以恢復數據的最小指令集,可以使用命令bgrewriteaof;
  2. 重寫原理:AOF文件持續增長而過大時,會fork出一條新進程來將文件重寫(也是先寫臨時文件最后再rename),遍歷新進
    程的內存中數據;重寫aof文件的操作,並沒有讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一
    個新的aof文件,這點和快照有點類似;
  3. 觸發機制:Redis會記錄上次重寫時的AOF大小,默認配置是當AOF文件大小是上次rewrite后大小的一倍且文件大於64M時觸發;
3.2.3 AOF 優勢和劣勢
  • 優勢:
    • 同步持久化,appendfsync=always;
    • 每修改同步,appendfsync=everysec;
  • 劣勢:
    • 相同數據集的數據而言,aof文件要遠大於rdb文件,恢復速度慢於rdb;
    • AOF運行效率要慢於rdb,每秒同步策略效率較好,不同步效率和rdb相同;

3.3 RDB 和 AOF

  1. RDB 持久化方式能夠在指定的時間間隔,對數據進行快照存儲;
  2. AOF 持久化方式記錄每次對服務器寫的操作,當服務器重啟的時候,會重新執行這些命令來恢復原始的數據,AOF 命令
    以redis協議追加保存每次寫的操作到文件末尾,Redis 還能對AOF文件進行后台重寫,使得AOF文件的體積不至於過大;
  3. 同時開啟兩種持久化方式:
    • 在這種情況下,當redis重啟的時候,會優先加載AOF文件來恢復原始的數據,因為在通常情況下,AOF文件保存的數
      據集要比RDB文件保存的數據集完整;

4. Redis 的事務

4.1 事務
- 事務本質上是一組命令的集合;一個事務中的所有命令都會序列化,按順序地串行化執行而不會被其他命令插入,不許加塞;
4.2 常用命令
- MULTI: 標記一個事務塊的開始;
- EXEC: 執行所有事務塊內的命令;
- DISCARD: 取消事務,放棄執行事務塊內的所有命令;
- WATCH: 監控一個(或多個)key,如果在事務執行之前,這個(或這些)key被其他命令所改動,那么事務將被打斷;
- UNWATCH: 取消WATCH命令對所有key的監視;

4.3 事務的執行情況:
- 正常執行
- 放棄事務
- 全體連坐:不管命令正確錯誤,都不執行;
- 冤頭債主:錯誤的命令不執行,正確的命令執行;
- watch監控

4.4 Redis 事務的三階段
- 開啟: 以MULTI開始一個事務;
- 入隊: 將多個命令入隊到事務中,接到這些命令並不會立即執行,而是放到等待執行的事務隊列里面;
- 執行: 以EXEC命令觸發事務;

4.5 Redis 事務的三個特性
- 單獨的隔離操作:事務中的所有命令都會序列化,按順序地執行;事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷;
- 沒有隔離級別的概念:隊列中的命令,在沒有提交之前不會實際的被執行,因為事務提交前任何指令都不會被實際執行,
也就不存在"事務內的查詢要看到事務里的更新,在事務外查詢不能看到"這個讓人萬分頭痛的問題;
- 不保證原子性:redis同一個事務中,如果有一條命令執行失敗,其他的命令仍然會被執行,沒有回滾;

5. Redis 的發布/訂閱

  • Redis 的發布/訂閱:進程間的一種信息通信模式,發送者(pub)發送消息,訂閱者(sub)接收消息;

6. Redis 主從復制(Master/Slave)

6.1 概述
  • 主從復制: 主機數據更新后,根據配置和策略,自動同步到備機的master/slave機制,Master以寫為主,Slave以讀為主;
6.2 應用
  1. 讀寫分離
  2. 容災備份
6.3 使用方法
  1. 配從(庫)不配主(庫);

  2. 從庫配置: slaveof 主庫IP 主庫端口

    • 每次與master斷開之后,都需要重新連接,除非配置進redis.conf文件;
    • info replication: 查看當前庫的狀態;
  3. 修改配置文件

    • 拷貝多個 redis.conf 文件;
    • 設置 daemonize yes, Pid 文件名字, 指定端口, Log文件的名字, Dump.rbd名字;
  4. 常用3招

    • 一主二從(一個Master,兩個Slave)
    • 薪火相傳
      • 上一個Slave可以是下一個Slave的Master,Slave同樣可以接收其他Slaves的連接和同步請求,那么,該
        Slave就是下一個Slave的Master,這樣,可以有效減輕master的寫壓力;
      • 中途變更轉向:會清除之前的數據,重新建立拷貝最新的; slaveof 新主庫IP 新主庫端口;
    • 反客為主
      • slaveof no one
      • 使當前數據庫停止與其他數據庫的同步,轉成主數據庫;
  5. 復制原理

    • slave 成功連接到master后,會發送一個sync命令;
    • Master 接到命令,啟動后台的存盤進程,同時,收集所有接收到的用於修改數據集命令,在后台進程執行完畢之后,
      master 將傳送整個數據文件到slave,以完成一次完全同步;
    • 全量復制:slave 服務在接收到數據庫文件數據后,將其存盤並加載到內存中;
    • 增量復制:Master繼續將新的所有收集到的修改命令依次傳給slave,完成同步;
    • 但是,只要是重新連接master,一次完全同步(全量復制)將被自動執行;
    • 復制的缺點:復制的延時;由於所有的寫操作都是先在Master上操作,然后同步更新到Slave上,所以從Master同步
      到slave機器有一定的延遲,當系統很繁忙的時候,延遲問題會更加嚴重,Slave機器數量的增加也會使這個問題更加嚴重;
  6. 哨兵模式(Sentinel)

    • "反客為主"的自動版,能夠后台監控主機是否故障,如果發生故障,根據投票數自動將從庫轉換為主庫;
    • 使用步驟
    • 一個sentinel能同時監控多個Master;

7. Jedis 使用

7.1 導 jar 包
  • Commons-pool-1.6.jar
  • Jedis-2.1.0.jar
7.2 編寫測試類
// TestJedis.java
public class TestJedis{

    public static void main(String[] args){

        // 建立連接
        Jedis jedis = new Jedis("127.0.0.1",6379);

        // 1. 測試連通性
        System.out.println(jedis.ping());

        // 2. Redis 常用API
        // 執行添加操作
        jedis.set("k1","v1");
        jedis.set("k2","v2");
        jedis.set("k3","v3");

        // 獲取
        System.out.println(jedis.get("k1"));

        // 獲取所有
        Set<String> sets = jedis.keys("*"))
        System.out.println(sets);

        // 3. 事務操作
        // 開啟事務
        Transaction transaction = jedis.multi();

        transaction.set("k11","v11");
        transaction.set("k12","v12");

        // 提交事務: transaction.exec();
        // 回滾事務:
        transaction.discard();

        // 3.1 事務加鎖(watch 命令)
        // watch 命令就是標記一個鍵,如果標記了一個鍵,在提交事務前,如果該鍵被別人修改過,那事務
        // 就會失敗,這種情況通常可以在程序中重新再嘗試一次;
        TestJedis test = new TestJedis();
        boolean retValue = test.transMethod();
        System.out.println("事務執行情況:"+retValue);

        // 4. 主從復制(主寫,從讀)
        Jedis jedis_M = new Jedis("127.0.0.1",6380);
        Jedis jedis_S = new Jedis("127.0.0.1",6381);

        jedis_S.slaveof("127.0.0.1",6380);

        jedis_M.set("name","zhangsan");
        System.out.println(jedis_S.get("name"));
    }

    // 3.1 事務中調用的方法
    public boolean transMethod(){
        Jedis jedis = new Jedis("127.0.0.1",6379);
        int balance; //可用余額
        int debt; // 欠額
        int amtToSubtract = 10; // 實刷額度

        // watch 監控
        jedis.watch("balance");
        balace = Integer.parseInt(jedis.get("balance"));
        if(balance < amtToSubtract){
            jedis.unwatch();
            System.out.println("余額不足,請充值");
            return false;
        } else {
            System.out.println("#############transaction 開始");
            Transaction transaction = jedis.multi();
            transaction.decrBy("balance",amtToSubtract);
            transaction.incrBy("debt",amtToSubtract);
            transaction.exec();
            balance = Integer.parseInt(jedis.get("balance"));
            debt = Integer.parseInt(jedis.get("debt"));

            System.out.println("########"+balace);
            System.out.println("##########"+debt);
            return true;
        }
    }
}
7.3 JedisPool
// JedisPoolUtil.java
public class JedisPoolUtil{

    // 連接池,單例設計模式
    private static volatile JedisPool jedisPool = null;

    private JedisPoolUtil(){};

    public static JedisPool getJedisPoolInstance(){

        if(null == jedisPool){
            // 同步代碼塊
            synchronized(jedisPoolUtil.class){
                if(null == jedisPool){
                    // 連接池配置
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    poolConfig.setMaxActive(1000);
                    poolConfig.setMaxIdle(32);
                    poolConfig.setMaxWait(100*1000);

                    // 獲得一個jedis實例的時候,是否檢查連接可用性
                    poolConfig.setTestOnBorrow(true);

                    jedisPool = new JedisPool(poolConfig,'127.0.0.1',6379);
                }
            }
        }

        return jedisPool;
    }

    // 將連接歸還連接池
    public static void release(JedisPool jedisPool,Jedis jedis){
        if(null != jedis){
            jedisPool.returnResourceObject(jedis);
        }
    }
}

// 測試類
public class TestPool{
    public static void main(String[] args){

        JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
        Jedis jedis = null;

        try{
            jedis = jedisPool.getResrouce();
            jedis.set("name","lisi");

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            // 釋放連接
            jedisPoolUtil.release(jedisPool,jedis);
        }
    }
}

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM