【進階篇】Redis實戰之Jedis使用技巧詳解

一、摘要

在上一篇文章中,我們詳細的介紹了 redis 的安裝和常見的操作命令,以及可視化工具的介紹。

剛知道服務端的操作知識,還是遠遠不夠的,如果想要真正在項目中得到應用,我們還需要一個 redis 的客戶端,然後將其集成到項目中,讓程序自動根據我們的業務需要自動處理。

基於 redis 開放的通信協議,大神們紛紛開發了各種語言的 redis 客戶端,有 c、c++、java、python、php、nodeJs 等等開發語言的客戶端,準確來說其實這些客戶端都是基於 redis 命令做了一層封裝,然後打包成工具以便大家更佳方便的操作 redis,以 Java 項目爲例,使用最廣的就是以下三種客戶端:

  • Jedis
  • Lettuce
  • Redisson

由於篇幅的原因,我們分三篇文章來詳細的講解每個客戶端的使用方式以及它的優缺點。

廢話不多說,直奔主題!

二、Jedis

Jedis 是老牌的 Redis 的 Java 客戶端,提供了比較全面的 Redis 命令的操作支持,也是目前使用最廣泛的客戶端。

官方網址如下:

https://github.com/redis/jedis

如何在項目中集成 Jedis 呢?請看下文!

2.1、基本使用

首先創建一個普通的 Maven 項目,然後添加Jedis依賴包!

<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>3.9.0</version>
</dependency>

然後創建一個簡單的測試,即可實現連接!

public class JedisMain {

    public static void main(String[] args) {
        // 1.構造一個 Jedis 對象,因爲這裏使用的默認端口 6379,所以不用配置端口
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        // 2.密碼認證
        jedis.auth("111111");
        // 3.測試是否連接成功
        String ping = jedis.ping();
        // 4.返回 pong 表示連接成功
        System.out.println(ping);
    }
}

對於 Jedis 而言,一旦連接上了 Redis 服務器,剩下的操作就非常容易了,由於 Jedis 中的 API 和 Redis 的命令高度一致,所以,Jedis 中的方法見名知意,直接使用即可。

2.2、連接池

雖然 redis 服務端是單線程操作,但是在實際項目中,使用 Jedis 對象來操作 redis 時,每次操作都需要新建/關閉 TCP 連接,連接資源開銷很高,同時 Jedis 對象的個數不受限制,在極端情況下可能會造成連接泄漏,同時 Jedis 存在多線程不安全的問題。

爲什麼說 Jedis 線程不安全,更加詳細的原因可以訪問這個地址https://www.cnblogs.com/gxyandwmm/p/13485226.html

所以我們需要將 Jedis 交給線程池來管理,使用 Jedis 對象時,從連接池獲取 Jedis,使用完成之後,再還給連接池。

在使用之前,需要添加common-pool線程池依賴包!

<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-pool2</artifactId>
	<version>2.11.1</version>
</dependency>

創建一個簡單的使用線程池測試用例。

public class JedisPoolMain {

    public static void main(String[] args) {
        // 1. 構造一個 Jedis 連接池
        JedisPool pool = new JedisPool("127.0.0.1", 6379);
        // 2. 從連接池中獲取一個 Jedis 連接
        Jedis jedis = pool.getResource();
        jedis.auth("111111");
        // 3. Jedis 操作
        String ping = jedis.ping();
        System.out.println(ping);
        // 4. 歸還連接
        jedis.close();
    }
}

2.3、連接池配置

在實際的使用過程中,我們常常會這樣來初始化線程池JedisPool,詳細代碼如下:

public class RedisPoolUtils {

    private static JedisPool jedisPool = null;

    /**
     * redis服務器地址
     */
    private static String addr = "127.0.0.1";

    /**
     * redis服務器端口
     */
    private static int port = 6379;

    /**
     * redis服務器密碼
     */
    private static String auth = "111111";


    static{
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            //連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true
            config.setBlockWhenExhausted(true);
            //設置的逐出策略類名, 默認DefaultEvictionPolicy(當連接超過最大空閒時間,或連接數超過最大空閒連接數)
            config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
            //是否啓用pool的jmx管理功能, 默認true
            config.setJmxEnabled(true);
            //MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默認爲"pool", JMX不熟,具體不知道是幹啥的...默認就好.
            config.setJmxNamePrefix("pool");
            //是否啓用後進先出, 默認true
            config.setLifo(true);
            //最大空閒連接數, 默認8個
            config.setMaxIdle(8);
            //最大連接數, 默認8個
            config.setMaxTotal(8);
            //獲取連接時的最大等待毫秒數(如果設置爲阻塞時BlockWhenExhausted),如果超時就拋異常, 小於零:阻塞不確定的時間,  默認-1
            config.setMaxWaitMillis(-1);
            //逐出連接的最小空閒時間 默認1800000毫秒(30分鐘)
            config.setMinEvictableIdleTimeMillis(1800000);
            //最小空閒連接數, 默認0
            config.setMinIdle(0);
            //每次逐出檢查時 逐出的最大數目 如果爲負數就是 : 1/abs(n), 默認3
            config.setNumTestsPerEvictionRun(3);
            //對象空閒多久後逐出, 當空閒時間>該值 且 空閒連接>最大空閒數 時直接逐出,不再根據MinEvictableIdleTimeMillis判斷  (默認逐出策略)
            config.setSoftMinEvictableIdleTimeMillis(1800000);
            //在獲取連接的時候檢查有效性, 默認false
            config.setTestOnBorrow(false);
            //在空閒時檢查有效性, 默認false
            config.setTestWhileIdle(false);
            //逐出掃描的時間間隔(毫秒) 如果爲負數,則不運行逐出線程, 默認-1
            config.setTimeBetweenEvictionRunsMillis(-1);
            jedisPool = new JedisPool(config, addr, port, 3000, auth);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 獲取 Jedis 資源
     * @return
     */
    public static Jedis getJedis() {
        if (jedisPool != null) {
            return jedisPool.getResource();
        }
        return null;
    }

    /**
     * 釋放Jedis資源
     */
    public static void close(final Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

}

簡單測試

public static void main(String[] args) throws InterruptedException {
    //獲取 jedis 客戶端
    Jedis jedis = RedisPoolUtils.getJedis();

    System.out.println("清空數據:"+jedis.flushDB());
    System.out.println("判斷某個鍵是否存在:"+jedis.exists("username"));
    System.out.println("新增<'username','xmr'>的鍵值對:"+jedis.set("username", "xmr"));
    System.out.println(jedis.exists("username"));
    System.out.println("新增<'password','password'>的鍵值對:"+jedis.set("password", "123"));
    System.out.print("系統中所有的鍵如下:");
    Set<String> keys = jedis.keys("*");
    System.out.println(keys);
    System.out.println("刪除鍵password:"+jedis.del("password"));
    System.out.println("判斷鍵password是否存在:"+jedis.exists("password"));
    System.out.println("設置鍵username的過期時間爲5s:"+jedis.expire("username", 8L));
    TimeUnit.SECONDS.sleep(1);
    System.out.println("查看鍵username的剩餘生存時間:"+jedis.ttl("username"));
    System.out.println("移除鍵username的生存時間:"+jedis.persist("username"));
    System.out.println("查看鍵username的剩餘生存時間:"+jedis.ttl("username"));
    System.out.println("查看鍵username所存儲的值的類型:"+jedis.type("username"));
    RedisPoolUtils.close(jedis);
}

運行結果如下:

清空數據:OK
判斷某個鍵是否存在:false
新增<'username','xmr'>的鍵值對:OK
true
新增<'password','password'>的鍵值對:OK
系統中所有的鍵如下:[password, username]
刪除鍵password:1
判斷鍵password是否存在:false
設置鍵username的過期時間爲5s:1
查看鍵username的剩餘生存時間:7
移除鍵username的生存時間:1
查看鍵username的剩餘生存時間:-1
查看鍵username所存儲的值的類型:string

2.4、字符串常用 API 操作

public class RedisClientUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);


    /**
     * 獲取指定key的值,如果key不存在返回null
     * 返回值:返回 key 的值,如果 key 不存在時,返回 nil
     * @param key
     * @return
     */
    public static String get(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(key);
        } catch (Exception e){
            log.error("get命令操作失敗,請求參數:{}", key,e);
        }
        return null;
    }


    /**
     * 設置key的值爲value
     * 返回值:操作成功完成時返回 OK
     * @param key
     * @return
     */
    public static String set(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.set(key, value);
        } catch (Exception e){
            log.error("set命令操作失敗,參數key:{},參數value:{}", key, value,e);
        }
        return null;
    }


    /**
     * 刪除指定的key,返回值:被刪除 key 的數量
     * 返回值:被刪除 key 的數量
     * @param key
     * @return
     */
    public static Long del(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            Long result = jedis.del(key);
            return jedis.del(key);
        } catch (Exception e){
            log.error("del命令操作失敗,參數key:{}", key,e);
        }
        return 0L;
    }


    /**
     * 通過key向指定的value值追加值
     * 返回值:追加指定值之後, key中字符串的長度
     * @param key
     * @return
     */
    public static Long append(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.append(key, value);
        } catch (Exception e){
            log.error("append命令操作失敗,參數key:{},參數value:{}", key, value,e);
        }
        return 0L;
    }

    /**
     * 判斷key是否存在
     * 返回值:true/false
     * @param key
     * @return
     */
    public static Boolean exists(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.exists(key);
        } catch (Exception e){
            log.error("exists命令操作失敗,參數key:{}", key,e);
        }
        return false;
    }


    /**
     * 設置key的超時時間爲seconds
     * 返回值:若 key 存在返回 1 ,否則返回 0
     * @param key
     * @return
     */
    public static Long expire(String key, long seconds) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.expire(key, seconds);
        } catch (Exception e){
            log.error("expire命令操作失敗,參數key:{},參數seconds:{}", key, seconds,e);
        }
        return 0L;
    }

    /**
     * 返回 key 的剩餘過期時間(單位秒)
     * 返回值:當 key 不存在時,返回 -2 。 當 key 存在但沒有設置剩餘生存時間時,返回 -1 。 否則,以秒爲單位,返回 key 的剩餘生存時間
     * @param key
     * @return
     */
    public static Long ttl(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.ttl(key);
        } catch (Exception e){
            log.error("ttl命令操作失敗,參數key:{}", key,e);
        }
        return 0L;
    }


    /**
     * 設置指定key的值爲value,當key不存在時才設置
     * 返回值:設置成功返回 1,設置失敗返回 0
     * @param key
     * @return
     */
    public static Long setnx(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.setnx(key, value);
        } catch (Exception e){
            log.error("setnx命令操作失敗,參數key:{},參數value:{}", key, value,e);
        }
        return 0L;
    }

    /**
     * 設置指定key的值爲value,並設置過期時間
     * 返回值:設置成功時返回 OK
     * @param key
     * @return
     */
    public static String setex(String key, String value, long seconds) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.setex(key, seconds, value);
        } catch (Exception e){
            log.error("setex命令操作失敗,參數key:{},參數value:{}", key, value,e);
        }
        return null;
    }

    /**
     * 通過key 和offset 從指定的位置開始將原先value替換
     * 返回值:被修改後的字符串長度
     * @param key
     * @return
     */
    public static Long setrange(String key, int offset, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.setrange(key, offset, value);
        } catch (Exception e){
            log.error("setrange命令操作失敗,參數key:{},參數value:{},參數offset:{}", key, value, offset,e);
        }
        return null;
    }


    /**
     * 通過批量的key獲取批量的value
     * 返回值:一個包含所有給定 key 的值的列表。
     * @param keys
     * @return
     */
    public static List<String> mget(String... keys) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.mget(keys);
        } catch (Exception e){
            log.error("mget命令操作失敗,參數key:{}", keys.toString(),e);
        }
        return null;
    }

    /**
     * 批量的設置key:value,也可以一個
     * 返回值:總是返回 OK
     * @param keysValues
     * @return
     */
    public static String mset(String... keysValues) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.mset(keysValues);
        } catch (Exception e){
            log.error("mset命令操作失敗,參數key:{}", keysValues.toString(),e);
        }
        return null;
    }


    /**
     * 設置key的值,並返回一箇舊值
     * 返回值:返回給定 key 的舊值,當 key 沒有舊值時,即 key 不存在時,返回 nil
     * @param key
     * @return
     */
    public static String getSet(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.getSet(key, value);
        } catch (Exception e){
            log.error("getSet命令操作失敗,參數key:{},參數value:{}", key, value,e);
        }
        return null;
    }

    /**
     * 通過下標和 key 獲取指定下標位置的 value
     * 返回值:截取得到的子字符串
     * @param key
     * @return
     */
    public static String getrange(String key, int startOffset, int endOffset) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.getrange(key, startOffset, endOffset);
        } catch (Exception e){
            log.error("getrange命令操作失敗,參數key:{},參數startOffset:{},參數offset:{}", key, startOffset, endOffset,e);
        }
        return null;
    }


    /**
     * 通過key 對value進行加值+1操作,當value不是int類型時會返回錯誤,當key不存在是則value爲1
     * 返回值:執行INCR命令之後 key 的值
     * @param key
     * @return
     */
    public static Long incr(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.incr(key);
        } catch (Exception e){
            log.error("incr命令操作失敗,參數key:{}", key, e);
        }
        return 0L;
    }


    /**
     * 通過key給指定的value加值
     * 返回值:執行INCR命令之後 key 的值
     * @param key
     * @return
     */
    public static Long incrBy(String key, long increment) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.incrBy(key, increment);
        } catch (Exception e){
            log.error("incrBy命令操作失敗,參數key:{},參數increment:{}", key, increment,e);
        }
        return 0L;
    }

    /**
     * 對key的值做減減操作
     * 返回值:執行INCR命令之後 key 的值
     * @param key
     * @return
     */
    public static Long decr(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.decr(key);
        } catch (Exception e){
            log.error("decr命令操作失敗,參數key:{}", key, e);
        }
        return 0L;
    }

    /**
     * 對key的值做減減操作,減去指定的值
     * 返回值:執行INCR命令之後 key 的值
     * @param key
     * @return
     */
    public static Long decrBy(String key, long decrement) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.decrBy(key, decrement);
        } catch (Exception e){
            log.error("decrBy命令操作失敗,參數key:{},參數decrement:{}", key, decrement,e);
        }
        return 0L;
    }


    /**
     * 通過key獲取value值的長度
     * 返回值:value值的長度
     * @param key
     * @return
     */
    public static Long strlen(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.strlen(key);
        } catch (Exception e){
            log.error("strlen命令操作失敗,參數key:{}", key, e);
        }
        return 0L;
    }
}

2.5、哈希常用 API 操作

public class RedisClientUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);


    /**
     * 通過key 和 field 獲取指定的 value
     * 返回值:對應的value值
     * @param key
     * @return
     */
    public static String hget(String key, String field) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hget(key, field);
        } catch (Exception e){
            log.error("hget命令操作失敗,參數key:{},參數field:{}", key, field,e);
        }
        return null;
    }

    /**
     * 通過key給field設置指定的值,如果key不存在,則先創建
     * 返回值:如果字段是哈希表中的一個新建字段,並且值設置成功,返回 1 ;如果哈希表中域字段已經存在且舊值已被新值覆蓋,返回 0 。
     * @param key
     * @return
     */
    public static Long hset(String key, String field, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hset(key, field, value);
        } catch (Exception e){
            log.error("hset命令操作失敗,參數key:{},參數field:{},參數value:{}", key, field, value,e);
        }
        return 0L;
    }


    /**
     * 通過key和field判斷是否有指定的value存在
     * 返回值:true/false
     * @param key
     * @return
     */
    public static Boolean hexists(String key, String field) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hexists(key, field);
        } catch (Exception e){
            log.error("hexists命令操作失敗,參數key:{},參數field:{}", key, field,e);
        }
        return false;
    }


    /**
     * 通過key返回field的數量
     * 返回值:field的數量
     * @param key
     * @return
     */
    public static Long hlen(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hlen(key);
        } catch (Exception e){
            log.error("hlen命令操作失敗,參數key:{}", key,e);
        }
        return 0L;
    }


    /**
     * 通過key 刪除指定的 field
     * 返回值:刪除的數量
     * @param key
     * @return
     */
    public static Long hdel(String key, String... fields) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hdel(key, fields);
        } catch (Exception e){
            log.error("hdel命令操作失敗,參數key:{},參數fields:{}", key, fields.toString(),e);
        }
        return 0L;
    }


    /**
     * 通過key返回所有的field
     * 返回值:field集合
     * @param key
     * @return
     */
    public static Set<String> hkeys(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hkeys(key);
        } catch (Exception e){
            log.error("hkeys命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }


    /**
     * 通過key獲取所有的field和value
     * 返回值:map對象
     * @param key
     * @return
     */
    public static Map<String, String> hgetAll(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hgetAll(key);
        } catch (Exception e){
            log.error("hgetAll命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }
}

2.6、列表常用 API 操作

public class RedisClientUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);


    /**
     * 過key向list頭部添加字符串
     * 返回值:執行 LPUSH 命令後,列表的長度
     * @param key
     * @return
     */
    public static Long lpush(String key, String... strs) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.lpush(key, strs);
        } catch (Exception e){
            log.error("lpush命令操作失敗,參數key:{},參數strs:{}", key, strs.toString(),e);
        }
        return null;
    }


    /**
     * 通過key向list尾部添加字符串
     * 返回值:執行 RPUSH 命令後,列表的長度
     * @param key
     * @return
     */
    public static Long rpush(String key, String... strs) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.rpush(key, strs);
        } catch (Exception e){
            log.error("rpush命令操作失敗,參數key:{},參數strs:{}", key, strs.toString(),e);
        }
        return null;
    }

    /**
     * 通過key設置list指定下標位置的value 如果下標超過list裏面value的個數則報錯
     * 返回值:操作成功返回 ok ,否則返回錯誤信息
     * @param key
     * @return
     */
    public static String lset(String key, Long index, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.lset(key, index, value);
        } catch (Exception e){
            log.error("lset命令操作失敗,參數key:{},參數index:{},參數value:{}", key, index, value,e);
        }
        return null;
    }


    /**
     * 通過key從對應的list中刪除指定的count個 和 value相同的元素
     * 返回值:返回被刪除的個數
     * @param key
     * @return
     */
    public static Long lrem(String key, long count, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.lrem(key, count, value);
        } catch (Exception e){
            log.error("lrem命令操作失敗,參數key:{},參數count:{},參數value:{}", key, count, value,e);
        }
        return null;
    }



    /**
     * 通過key保留list中從strat下標開始到end下標結束的value值
     * 返回值:操作成功返回 ok ,否則返回錯誤信息
     * @param key
     * @return
     */
    public static String ltrim(String key, long start, long end) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.ltrim(key, start, end);
        } catch (Exception e){
            log.error("ltrim命令操作失敗,參數key:{},參數start:{},參數end:{}", key, start, end,e);
        }
        return null;
    }


    /**
     * 通過key從list的頭部刪除一個value,並返回該value
     * 返回值:value值
     * @param key
     * @return
     */
    public static String lpop(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.lpop(key);
        } catch (Exception e){
            log.error("lpop命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }

    /**
     * 通過key從list尾部刪除一個value,並返回該元素
     * 返回值:value值
     * @param key
     * @return
     */
    public static String rpop(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.rpop(key);
        } catch (Exception e){
            log.error("rpop命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }


    /**
     * 通過key獲取list中指定下標位置的value
     * 返回值:value值
     * @param key
     * @return
     */
    public static String lindex(String key, long index){
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.lindex(key, index);
        } catch (Exception e){
            log.error("lindex命令操作失敗,參數key:{},參數index:{}", key, index,e);
        }
        return null;
    }


    /**
     * 通過key返回list的長度
     * 返回值:value值
     * @param key
     * @return
     */
    public static Long llen(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.llen(key);
        } catch (Exception e){
            log.error("llen命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }


    /**
     * 通過key獲取list指定下標位置的value 如果start 爲 0 end 爲 -1 則返回全部的list中的value
     * 返回值:value值
     * @param key
     * @return
     */
    public static List<String> lrange(String key, long start, long end) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.lrange(key, start, end);
        } catch (Exception e){
            log.error("lrange命令操作失敗,參數key:{},參數start:{},參數end:{}", key, start, end,e);
        }
        return null;
    }

}

2.7、集合常用 API 操作

public class RedisClientUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);


    /**
     * 通過key向指定的set中添加value
     * 返回值:添加成功的個數
     * @param key
     * @return
     */
    public static Long sadd(String key, String... members)  {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.sadd(key, members);
        } catch (Exception e){
            log.error("sadd命令操作失敗,參數key:{},參數members:{}", key, members.toString(),e);
        }
        return null;
    }

    /**
     * 通過key刪除set中對應的value值
     * 返回值:刪除成功的個數
     * @param key
     * @return
     */
    public static Long srem(String key, String... members)  {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.srem(key, members);
        } catch (Exception e){
            log.error("srem命令操作失敗,參數key:{},參數members:{}", key, members.toString(),e);
        }
        return null;
    }

    /**
     * 通過key獲取set中value的個數
     * 返回值:value的個數
     * @param key
     * @return
     */
    public static Long scard(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.scard(key);
        } catch (Exception e){
            log.error("scard命令操作失敗,參數key:{}", key,e);
        }
        return 0L;
    }


    /**
     * 通過key判斷value是否是set中的元素
     * 返回值:true/false
     * @param key
     * @return
     */
    public static Boolean sismember(String key, String member) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.sismember(key, member);
        } catch (Exception e){
            log.error("sismember命令操作失敗,參數key:{},參數member:{}", key, member,e);
        }
        return false;
    }


    /**
     * 通過key獲取set中所有的value
     * 返回值:所有的value
     * @param key
     * @return
     */
    public static Set<String> smembers(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.smembers(key);
        } catch (Exception e){
            log.error("smembers命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }

}

2.8、有序集合常用 API 操作

public class RedisClientUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisClientUtil.class);

    /**
     * 通過key向zset中添加value,score,其中score就是用來排序的 如果該value已經存在則根據score更新元素
     * 返回值:被成功添加的新成員的數量,不包括那些被更新的、已經存在的成員
     * @param key
     * @return
     */
    public static Long zadd(String key, double score, String member) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zadd(key, score, member);
        } catch (Exception e){
            log.error("zadd命令操作失敗,參數key:{},參數score:{},參數member:{}", key, score, member,e);
        }
        return null;
    }


    /**
     * 通過key刪除在zset中指定的value
     * 返回值:刪除個數
     * @param key
     * @return
     */
    public static Long zrem(String key, String... members) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zrem(key, members);
        } catch (Exception e){
            log.error("zrem命令操作失敗,參數key:{},參數members:{}", key, members.toString(),e);
        }
        return null;
    }


    /**
     * 通過key增加該zset中value的score的值
     * 返回值:member 成員的新分數值
     * @param key
     * @return
     */
    public static Double zincrby(String key, double score, String member) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zincrby(key, score, member);
        } catch (Exception e){
            log.error("zincrby命令操作失敗,參數key:{},參數score:{},參數member:{}", key, score, member,e);
        }
        return null;
    }

    /**
     * 通過key返回zset中value的排名 下標從小到大排序
     * 返回值:返回 member 的排名
     * @param key
     * @return
     */
    public static Long zrank(String key, String member) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zrank(key, member);
        } catch (Exception e){
            log.error("zrank命令操作失敗,參數key:{},參數member:{}", key, member,e);
        }
        return null;
    }


    /**
     * 通過key將獲取score從start到end中zset的value socre從大到小排序 當start爲0 end爲-1時返回全部
     * 返回值:返回 member 集合
     * @param key
     * @return
     */
    public static Set<String> zrevrange(String key, long start, long end) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zrevrange(key, start, end);
        } catch (Exception e){
            log.error("zrevrange命令操作失敗,參數key:{},參數start:{},參數end:{}", key, start, end,e);
        }
        return null;
    }

    /**
     * 返回指定區間內zset中value的數量
     * 返回值:返回 member 集合
     * @param key
     * @return
     */
    public static Long zcount(String key, String min, String max)  {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zcount(key, min, max);
        } catch (Exception e){
            log.error("zcount命令操作失敗,參數key:{},參數min:{},參數max:{}", key, min, max,e);
        }
        return null;
    }


    /**
     * 通過key返回zset中的value個數
     * 返回值:返回 member 集合
     * @param key
     * @return
     */
    public static Long zcard(String key)  {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zcard(key);
        } catch (Exception e){
            log.error("zcard命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }


    /**
     * 返回滿足pattern表達式的所有key keys(*) 返回所有的key
     * 返回值:返回 key 集合
     * @param pattern
     * @return
     */
    public static Set<String> keys(String pattern) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.keys(pattern);
        } catch (Exception e){
            log.error("keys命令操作失敗,參數pattern:{}", pattern,e);
        }
        return null;
    }

    /**
     * 通過key判斷值得類型
     * 返回值:值的類型
     * @param key
     * @return
     */
    public static String type(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.type(key);
        } catch (Exception e){
            log.error("type命令操作失敗,參數key:{}", key,e);
        }
        return null;
    }

}

三、集羣配置

在實際的項目生產環境中,redis 通常不是以單臺服務實例來運行的,因爲一旦服務器掛了,可能所有的下游服務都會受到影響,因此爲了保障單臺服務器即使出現故障也能運行,通常運維組會搭建集羣環境,來保證服務高可用。

搭建的方式有兩種,哨兵模式和 Cluster 模式。

  • 哨兵模式:對redis服務器進行監控,如果有宕機的,就從備機裏面選一個出來作爲主機,實現自動切換
  • Cluster 模式:將數據進行分片存儲,避免全部節點數據都一樣,浪費空間

3.1、哨兵模式

哨兵模式簡單的說,就是一臺主機,一臺備機,外加一臺監控服務,當監控服務觀測到主機已經宕機,就會將備用機切換成主機,以便繼續提供服務。

public class RedisPoolUtils {

    private static Jedis jedis;

    private static JedisSentinelPool jedisSentinelPool;

    static{
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            //最大空閒連接數, 默認8個
            config.setMaxIdle(8);
            //最大連接數, 默認8個
            config.setMaxTotal(8);
            //最小空閒連接數, 默認0
            config.setMinIdle(0);
            //獲取連接時的最大等待毫秒數(如果設置爲阻塞時BlockWhenExhausted),如果超時就拋異常, 小於零:阻塞不確定的時間,  默認-1
            config.setMaxWaitMillis(3000);
            //在獲取連接的時候檢查有效性,表示取出的redis對象可用, 默認false
            config.setTestOnBorrow(true);


            //redis服務器列表
            Set<String> sentinels = new HashSet<>();
            sentinels.add(new HostAndPort("192.168.43.212", 26379).toString());
            sentinels.add(new HostAndPort("192.168.43.213", 26379).toString());
            sentinels.add(new HostAndPort("192.168.43.214", 26379).toString());

            //初始化連接池
            jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels, config, "111111");
            // 從池中獲取一個Jedis對象
            jedis = jedisSentinelPool.getResource();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

3.2、集羣模式

爲了保證高可用,redis-cluster集羣通常會引入主從複製模型,一個主節點對應一個或者多個從節點,當主節點宕機的時候,就會啓用從節點。

public class RedisPoolUtils {

    static{
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            //最大空閒連接數, 默認8個
            config.setMaxIdle(8);
            //最大連接數, 默認8個
            config.setMaxTotal(8);
            //最小空閒連接數, 默認0
            config.setMinIdle(0);
            //獲取連接時的最大等待毫秒數(如果設置爲阻塞時BlockWhenExhausted),如果超時就拋異常, 小於零:阻塞不確定的時間,  默認-1
            config.setMaxWaitMillis(3000);
            //在獲取連接的時候檢查有效性,表示取出的redis對象可用, 默認false
            config.setTestOnBorrow(true);

            Set<HostAndPort> nodes = new HashSet<>();
            nodes.add(new HostAndPort("192.168.43.212", 26379));
            nodes.add(new HostAndPort("192.168.43.213", 26379));
            nodes.add(new HostAndPort("192.168.43.214", 26379));

            JedisCluster jedisCluster = new JedisCluster(nodes, config);
            jedisCluster.set("key", "hello world");

            jedisCluster.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、小結

jedis客戶端是目前使用最廣泛的一款 java 客戶端,也是老牌的 Redis 的 Java 實現客戶端。

優點很突出:

  • 比較全面的提供了 Redis 的操作特性,也就是說你能用 redis 命令操作的,Jedis 包都也給你封裝好了,直接使用即可
  • 使用廣泛,易上手

當然,缺點也有:

  • Jedis 客戶端實例不是線程安全的,需要藉助連接池來管理和使用 Jedis
  • 使用阻塞的I/O,且其方法調用都是同步的,程序流需要等到 sockets 處理完 I/O 才能執行,不支持異步

本文主要是圍繞jedis客戶端做了一次知識總結,希望能幫助到大家,內容難免有所遺漏,歡迎批評指出!

五、參考

1、redis - 中文文檔

2、騰訊雲 - redis幾種java客戶端比較

3、runoob - redis教程

4、簡書 - redis java 客戶端

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章