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

程式設計師志哥發表於2022-12-29

一、摘要

在上一篇文章中,我們詳細的介紹了 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 客戶端

相關文章