Redis實(shí)戰(zhàn)之Jedis使用技巧詳解
一、摘要
在上一篇文章中,我們詳細(xì)的介紹了 redis 的安裝和常見的操作命令,以及可視化工具的介紹。
剛知道服務(wù)端的操作知識,還是遠(yuǎn)遠(yuǎn)不夠的,如果想要真正在項(xiàng)目中得到應(yīng)用,我們還需要一個 redis 的客戶端,然后將其集成到項(xiàng)目中,讓程序自動根據(jù)我們的業(yè)務(wù)需要自動處理。
基于 redis 開放的通信協(xié)議,大神們紛紛開發(fā)了各種語言的 redis 客戶端,有 c、c++、java、python、php、nodeJs 等等開發(fā)語言的客戶端,準(zhǔn)確來說其實(shí)這些客戶端都是基于 redis 命令做了一層封裝,然后打包成工具以便大家更佳方便的操作 redis,以 Java 項(xiàng)目為例,使用最廣的就是以下三種客戶端:
- Jedis
- Lettuce
- Redisson
由于篇幅的原因,我們分三篇文章來詳細(xì)的講解每個客戶端的使用方式以及它的優(yōu)缺點(diǎn)。
廢話不多說,直奔主題!
二、Jedis
Jedis 是老牌的 Redis 的 Java 客戶端,提供了比較全面的 Redis 命令的操作支持,也是目前使用最廣泛的客戶端。
官方網(wǎng)址如下:
https://github.com/redis/jedis
如何在項(xiàng)目中集成 Jedis 呢?請看下文!
2.1、基本使用
首先創(chuàng)建一個普通的 Maven 項(xiàng)目,然后添加Jedis
依賴包!
<dependency> ??<groupId>redis.clients</groupId> ??<artifactId>jedis</artifactId> ??<version>3.9.0</version> </dependency>
然后創(chuàng)建一個簡單的測試,即可實(shí)現(xiàn)連接!
public?class?JedisMain?{ ????public?static?void?main(String[]?args)?{ ????????//?1.構(gòu)造一個?Jedis?對象,因?yàn)檫@里使用的默認(rèn)端口?6379,所以不用配置端口 ????????Jedis?jedis?=?new?Jedis("127.0.0.1",?6379); ????????//?2.密碼認(rèn)證 ????????jedis.auth("111111"); ????????//?3.測試是否連接成功 ????????String?ping?=?jedis.ping(); ????????//?4.返回?pong?表示連接成功 ????????System.out.println(ping); ????} }
對于 Jedis 而言,一旦連接上了 Redis 服務(wù)器,剩下的操作就非常容易了,由于 Jedis 中的 API 和 Redis 的命令高度一致,所以,Jedis 中的方法見名知意,直接使用即可。
2.2、連接池
雖然 redis 服務(wù)端是單線程操作,但是在實(shí)際項(xiàng)目中,使用 Jedis 對象來操作 redis 時,每次操作都需要新建/關(guān)閉 TCP 連接,連接資源開銷很高,同時 Jedis 對象的個數(shù)不受限制,在極端情況下可能會造成連接泄漏,同時 Jedis 存在多線程不安全的問題。
為什么說 Jedis 線程不安全,更加詳細(xì)的原因可以參考:使用Jedis面臨的非線程安全問題詳解
所以我們需要將 Jedis 交給線程池來管理,使用 Jedis 對象時,從連接池獲取 Jedis,使用完成之后,再還給連接池。
在使用之前,需要添加common-pool
線程池依賴包!
<dependency> ?<groupId>org.apache.commons</groupId> ?<artifactId>commons-pool2</artifactId> ?<version>2.11.1</version> </dependency>
創(chuàng)建一個簡單的使用線程池測試用例。
public?class?JedisPoolMain?{ ????public?static?void?main(String[]?args)?{ ????????//?1.?構(gòu)造一個?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、連接池配置
在實(shí)際的使用過程中,我們常常會這樣來初始化線程池JedisPool
,詳細(xì)代碼如下:
public?class?RedisPoolUtils?{ ????private?static?JedisPool?jedisPool?=?null; ????/** ?????*?redis服務(wù)器地址 ?????*/ ????private?static?String?addr?=?"127.0.0.1"; ????/** ?????*?redis服務(wù)器端口 ?????*/ ????private?static?int?port?=?6379; ????/** ?????*?redis服務(wù)器密碼 ?????*/ ????private?static?String?auth?=?"111111"; ????static{ ????????try?{ ????????????JedisPoolConfig?config?=?new?JedisPoolConfig(); ????????????//連接耗盡時是否阻塞,?false報(bào)異常,ture阻塞直到超時,?默認(rèn)true ????????????config.setBlockWhenExhausted(true); ????????????//設(shè)置的逐出策略類名,?默認(rèn)DefaultEvictionPolicy(當(dāng)連接超過最大空閑時間,或連接數(shù)超過最大空閑連接數(shù)) ????????????config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy"); ????????????//是否啟用pool的jmx管理功能,?默認(rèn)true ????????????config.setJmxEnabled(true); ????????????//MBean?ObjectName?=?new?ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name="?+?"pool"?+?i);?默認(rèn)為"pool",?JMX不熟,具體不知道是干啥的...默認(rèn)就好. ????????????config.setJmxNamePrefix("pool"); ????????????//是否啟用后進(jìn)先出,?默認(rèn)true ????????????config.setLifo(true); ????????????//最大空閑連接數(shù),?默認(rèn)8個 ????????????config.setMaxIdle(8); ????????????//最大連接數(shù),?默認(rèn)8個 ????????????config.setMaxTotal(8); ????????????//獲取連接時的最大等待毫秒數(shù)(如果設(shè)置為阻塞時BlockWhenExhausted),如果超時就拋異常,?小于零:阻塞不確定的時間,??默認(rèn)-1 ????????????config.setMaxWaitMillis(-1); ????????????//逐出連接的最小空閑時間?默認(rèn)1800000毫秒(30分鐘) ????????????config.setMinEvictableIdleTimeMillis(1800000); ????????????//最小空閑連接數(shù),?默認(rèn)0 ????????????config.setMinIdle(0); ????????????//每次逐出檢查時?逐出的最大數(shù)目?如果為負(fù)數(shù)就是?:?1/abs(n),?默認(rèn)3 ????????????config.setNumTestsPerEvictionRun(3); ????????????//對象空閑多久后逐出,?當(dāng)空閑時間>該值?且?空閑連接>最大空閑數(shù)?時直接逐出,不再根據(jù)MinEvictableIdleTimeMillis判斷??(默認(rèn)逐出策略) ????????????config.setSoftMinEvictableIdleTimeMillis(1800000); ????????????//在獲取連接的時候檢查有效性,?默認(rèn)false ????????????config.setTestOnBorrow(false); ????????????//在空閑時檢查有效性,?默認(rèn)false ????????????config.setTestWhileIdle(false); ????????????//逐出掃描的時間間隔(毫秒)?如果為負(fù)數(shù),則不運(yùn)行逐出線程,?默認(rèn)-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("清空數(shù)據(jù):"+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("系統(tǒng)中所有的鍵如下:"); ????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("設(shè)置鍵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); }
運(yùn)行結(jié)果如下:
清空數(shù)據(jù):OK
判斷某個鍵是否存在:false
新增<'username','xmr'>的鍵值對:OK
true
新增<'password','password'>的鍵值對:OK
系統(tǒng)中所有的鍵如下:[password, username]
刪除鍵password:1
判斷鍵password是否存在:false
設(shè)置鍵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命令操作失敗,請求參數(shù):{}",?key,e); ????????} ????????return?null; ????} ????/** ?????*?設(shè)置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命令操作失敗,參數(shù)key:{},參數(shù)value:{}",?key,?value,e); ????????} ????????return?null; ????} ????/** ?????*?刪除指定的key,返回值:被刪除?key?的數(shù)量 ?????*?返回值:被刪除?key?的數(shù)量 ?????*?@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命令操作失敗,參數(shù)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命令操作失敗,參數(shù)key:{},參數(shù)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命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?false; ????} ????/** ?????*?設(shè)置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命令操作失敗,參數(shù)key:{},參數(shù)seconds:{}",?key,?seconds,e); ????????} ????????return?0L; ????} ????/** ?????*?返回?key?的剩余過期時間(單位秒) ?????*?返回值:當(dāng)?key?不存在時,返回?-2?。?當(dāng)?key?存在但沒有設(shè)置剩余生存時間時,返回?-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命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?0L; ????} ????/** ?????*?設(shè)置指定key的值為value,當(dāng)key不存在時才設(shè)置 ?????*?返回值:設(shè)置成功返回?1,設(shè)置失敗返回?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命令操作失敗,參數(shù)key:{},參數(shù)value:{}",?key,?value,e); ????????} ????????return?0L; ????} ????/** ?????*?設(shè)置指定key的值為value,并設(shè)置過期時間 ?????*?返回值:設(shè)置成功時返回?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命令操作失敗,參數(shù)key:{},參數(shù)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命令操作失敗,參數(shù)key:{},參數(shù)value:{},參數(shù)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命令操作失敗,參數(shù)key:{}",?keys.toString(),e); ????????} ????????return?null; ????} ????/** ?????*?批量的設(shè)置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命令操作失敗,參數(shù)key:{}",?keysValues.toString(),e); ????????} ????????return?null; ????} ????/** ?????*?設(shè)置key的值,并返回一個舊值 ?????*?返回值:返回給定?key?的舊值,當(dāng)?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命令操作失敗,參數(shù)key:{},參數(shù)value:{}",?key,?value,e); ????????} ????????return?null; ????} ????/** ?????*?通過下標(biāo)和?key?獲取指定下標(biāo)位置的?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命令操作失敗,參數(shù)key:{},參數(shù)startOffset:{},參數(shù)offset:{}",?key,?startOffset,?endOffset,e); ????????} ????????return?null; ????} ????/** ?????*?通過key?對value進(jìn)行加值+1操作,當(dāng)value不是int類型時會返回錯誤,當(dāng)key不存在是則value為1 ?????*?返回值:執(zhí)行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命令操作失敗,參數(shù)key:{}",?key,?e); ????????} ????????return?0L; ????} ????/** ?????*?通過key給指定的value加值 ?????*?返回值:執(zhí)行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命令操作失敗,參數(shù)key:{},參數(shù)increment:{}",?key,?increment,e); ????????} ????????return?0L; ????} ????/** ?????*?對key的值做減減操作 ?????*?返回值:執(zhí)行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命令操作失敗,參數(shù)key:{}",?key,?e); ????????} ????????return?0L; ????} ????/** ?????*?對key的值做減減操作,減去指定的值 ?????*?返回值:執(zhí)行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命令操作失敗,參數(shù)key:{},參數(shù)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命令操作失敗,參數(shù)key:{}",?key,?e); ????????} ????????return?0L; ????} }
2.5、哈希常用 API 操作
public?class?RedisClientUtil?{ ????private?static?final?Logger?log?=?LoggerFactory.getLogger(RedisClientUtil.class); ????/** ?????*?通過key?和?field?獲取指定的?value ?????*?返回值:對應(yīng)的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命令操作失敗,參數(shù)key:{},參數(shù)field:{}",?key,?field,e); ????????} ????????return?null; ????} ????/** ?????*?通過key給field設(shè)置指定的值,如果key不存在,則先創(chuàng)建 ?????*?返回值:如果字段是哈希表中的一個新建字段,并且值設(shè)置成功,返回?1?;如果哈希表中域字段已經(jīng)存在且舊值已被新值覆蓋,返回?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命令操作失敗,參數(shù)key:{},參數(shù)field:{},參數(shù)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命令操作失敗,參數(shù)key:{},參數(shù)field:{}",?key,?field,e); ????????} ????????return?false; ????} ????/** ?????*?通過key返回field的數(shù)量 ?????*?返回值:field的數(shù)量 ?????*?@param?key ?????*?@return ?????*/ ????public?static?Long?hlen(String?key)?{ ????????try?(Jedis?jedis?=?jedisPool.getResource())?{ ????????????return?jedis.hlen(key); ????????}?catch?(Exception?e){ ????????????log.error("hlen命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?0L; ????} ????/** ?????*?通過key?刪除指定的?field ?????*?返回值:刪除的數(shù)量 ?????*?@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命令操作失敗,參數(shù)key:{},參數(shù)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命令操作失敗,參數(shù)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命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?null; ????} }
2.6、列表常用 API 操作
public?class?RedisClientUtil?{ ????private?static?final?Logger?log?=?LoggerFactory.getLogger(RedisClientUtil.class); ????/** ?????*?過key向list頭部添加字符串 ?????*?返回值:執(zhí)行?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命令操作失敗,參數(shù)key:{},參數(shù)strs:{}",?key,?strs.toString(),e); ????????} ????????return?null; ????} ????/** ?????*?通過key向list尾部添加字符串 ?????*?返回值:執(zhí)行?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命令操作失敗,參數(shù)key:{},參數(shù)strs:{}",?key,?strs.toString(),e); ????????} ????????return?null; ????} ????/** ?????*?通過key設(shè)置list指定下標(biāo)位置的value?如果下標(biāo)超過list里面value的個數(shù)則報(bào)錯 ?????*?返回值:操作成功返回?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命令操作失敗,參數(shù)key:{},參數(shù)index:{},參數(shù)value:{}",?key,?index,?value,e); ????????} ????????return?null; ????} ????/** ?????*?通過key從對應(yīng)的list中刪除指定的count個?和?value相同的元素 ?????*?返回值:返回被刪除的個數(shù) ?????*?@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命令操作失敗,參數(shù)key:{},參數(shù)count:{},參數(shù)value:{}",?key,?count,?value,e); ????????} ????????return?null; ????} ????/** ?????*?通過key保留list中從strat下標(biāo)開始到end下標(biāo)結(jié)束的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命令操作失敗,參數(shù)key:{},參數(shù)start:{},參數(shù)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命令操作失敗,參數(shù)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命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?null; ????} ????/** ?????*?通過key獲取list中指定下標(biāo)位置的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命令操作失敗,參數(shù)key:{},參數(shù)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命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?null; ????} ????/** ?????*?通過key獲取list指定下標(biāo)位置的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命令操作失敗,參數(shù)key:{},參數(shù)start:{},參數(shù)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 ?????*?返回值:添加成功的個數(shù) ?????*?@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命令操作失敗,參數(shù)key:{},參數(shù)members:{}",?key,?members.toString(),e); ????????} ????????return?null; ????} ????/** ?????*?通過key刪除set中對應(yīng)的value值 ?????*?返回值:刪除成功的個數(shù) ?????*?@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命令操作失敗,參數(shù)key:{},參數(shù)members:{}",?key,?members.toString(),e); ????????} ????????return?null; ????} ????/** ?????*?通過key獲取set中value的個數(shù) ?????*?返回值:value的個數(shù) ?????*?@param?key ?????*?@return ?????*/ ????public?static?Long?scard(String?key)?{ ????????try?(Jedis?jedis?=?jedisPool.getResource())?{ ????????????return?jedis.scard(key); ????????}?catch?(Exception?e){ ????????????log.error("scard命令操作失敗,參數(shù)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命令操作失敗,參數(shù)key:{},參數(shù)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命令操作失敗,參數(shù)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已經(jīng)存在則根據(jù)score更新元素 ?????*?返回值:被成功添加的新成員的數(shù)量,不包括那些被更新的、已經(jīng)存在的成員 ?????*?@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命令操作失敗,參數(shù)key:{},參數(shù)score:{},參數(shù)member:{}",?key,?score,?member,e); ????????} ????????return?null; ????} ????/** ?????*?通過key刪除在zset中指定的value ?????*?返回值:刪除個數(shù) ?????*?@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命令操作失敗,參數(shù)key:{},參數(shù)members:{}",?key,?members.toString(),e); ????????} ????????return?null; ????} ????/** ?????*?通過key增加該zset中value的score的值 ?????*?返回值:member?成員的新分?jǐn)?shù)值 ?????*?@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命令操作失敗,參數(shù)key:{},參數(shù)score:{},參數(shù)member:{}",?key,?score,?member,e); ????????} ????????return?null; ????} ????/** ?????*?通過key返回zset中value的排名?下標(biāo)從小到大排序 ?????*?返回值:返回?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命令操作失敗,參數(shù)key:{},參數(shù)member:{}",?key,?member,e); ????????} ????????return?null; ????} ????/** ?????*?通過key將獲取score從start到end中zset的value?socre從大到小排序?當(dāng)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命令操作失敗,參數(shù)key:{},參數(shù)start:{},參數(shù)end:{}",?key,?start,?end,e); ????????} ????????return?null; ????} ????/** ?????*?返回指定區(qū)間內(nèi)zset中value的數(shù)量 ?????*?返回值:返回?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命令操作失敗,參數(shù)key:{},參數(shù)min:{},參數(shù)max:{}",?key,?min,?max,e); ????????} ????????return?null; ????} ????/** ?????*?通過key返回zset中的value個數(shù) ?????*?返回值:返回?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命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?null; ????} ????/** ?????*?返回滿足pattern表達(dá)式的所有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命令操作失敗,參數(shù)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命令操作失敗,參數(shù)key:{}",?key,e); ????????} ????????return?null; ????} }
三、集群配置
在實(shí)際的項(xiàng)目生產(chǎn)環(huán)境中,redis 通常不是以單臺服務(wù)實(shí)例來運(yùn)行的,因?yàn)橐坏┓?wù)器掛了,可能所有的下游服務(wù)都會受到影響,因此為了保障單臺服務(wù)器即使出現(xiàn)故障也能運(yùn)行,通常運(yùn)維組會搭建集群環(huán)境,來保證服務(wù)高可用。
搭建的方式有兩種,哨兵模式和 Cluster 模式。
- 哨兵模式:對redis服務(wù)器進(jìn)行監(jiān)控,如果有宕機(jī)的,就從備機(jī)里面選一個出來作為主機(jī),實(shí)現(xiàn)自動切換
- Cluster 模式:將數(shù)據(jù)進(jìn)行分片存儲,避免全部節(jié)點(diǎn)數(shù)據(jù)都一樣,浪費(fèi)空間
3.1、哨兵模式
哨兵模式簡單的說,就是一臺主機(jī),一臺備機(jī),外加一臺監(jiān)控服務(wù),當(dāng)監(jiān)控服務(wù)觀測到主機(jī)已經(jīng)宕機(jī),就會將備用機(jī)切換成主機(jī),以便繼續(xù)提供服務(wù)。
public?class?RedisPoolUtils?{ ????private?static?Jedis?jedis; ????private?static?JedisSentinelPool?jedisSentinelPool; ????static{ ????????try?{ ????????????JedisPoolConfig?config?=?new?JedisPoolConfig(); ????????????//最大空閑連接數(shù),?默認(rèn)8個 ????????????config.setMaxIdle(8); ????????????//最大連接數(shù),?默認(rèn)8個 ????????????config.setMaxTotal(8); ????????????//最小空閑連接數(shù),?默認(rèn)0 ????????????config.setMinIdle(0); ????????????//獲取連接時的最大等待毫秒數(shù)(如果設(shè)置為阻塞時BlockWhenExhausted),如果超時就拋異常,?小于零:阻塞不確定的時間,??默認(rèn)-1 ????????????config.setMaxWaitMillis(3000); ????????????//在獲取連接的時候檢查有效性,表示取出的redis對象可用,?默認(rèn)false ????????????config.setTestOnBorrow(true); ????????????//redis服務(wù)器列表 ????????????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集群通常會引入主從復(fù)制模型,一個主節(jié)點(diǎn)對應(yīng)一個或者多個從節(jié)點(diǎn),當(dāng)主節(jié)點(diǎn)宕機(jī)的時候,就會啟用從節(jié)點(diǎn)。
public?class?RedisPoolUtils?{ ????static{ ????????try?{ ????????????JedisPoolConfig?config?=?new?JedisPoolConfig(); ????????????//最大空閑連接數(shù),?默認(rèn)8個 ????????????config.setMaxIdle(8); ????????????//最大連接數(shù),?默認(rèn)8個 ????????????config.setMaxTotal(8); ????????????//最小空閑連接數(shù),?默認(rèn)0 ????????????config.setMinIdle(0); ????????????//獲取連接時的最大等待毫秒數(shù)(如果設(shè)置為阻塞時BlockWhenExhausted),如果超時就拋異常,?小于零:阻塞不確定的時間,??默認(rèn)-1 ????????????config.setMaxWaitMillis(3000); ????????????//在獲取連接的時候檢查有效性,表示取出的redis對象可用,?默認(rèn)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(); ????????} ????} }
四、小結(jié)
jedis
客戶端是目前使用最廣泛的一款 java 客戶端,也是老牌的 Redis 的 Java 實(shí)現(xiàn)客戶端。
優(yōu)點(diǎn)很突出:
- 比較全面的提供了 Redis 的操作特性,也就是說你能用 redis 命令操作的,Jedis 包都也給你封裝好了,直接使用即可
- 使用廣泛,易上手
當(dāng)然,缺點(diǎn)也有:
- Jedis 客戶端實(shí)例不是線程安全的,需要借助連接池來管理和使用 Jedis
- 使用阻塞的I/O,且其方法調(diào)用都是同步的,程序流需要等到 sockets 處理完 I/O 才能執(zhí)行,不支持異步
到此這篇關(guān)于Redis實(shí)戰(zhàn)之Jedis使用技巧詳解的文章就介紹到這了,更多相關(guān)Redis Jedis內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
redis學(xué)習(xí)之RDB、AOF與復(fù)制時對過期鍵的處理教程
這篇文章主要給大家介紹了關(guān)于redis學(xué)習(xí)之RDB、AOF與復(fù)制時對過期鍵處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用redis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11redis實(shí)現(xiàn)分布式全局唯一id的示例代碼
在某些場景中,我們需要生成全局的唯一ID,本文主要介紹了redis實(shí)現(xiàn)分布式全局唯一id的示例代碼,具有一定的參考價值,感興趣的可以了解一下2024-04-04RedisDesktopManager遠(yuǎn)程連接redis的實(shí)現(xiàn)
本文主要介紹了RedisDesktopManager遠(yuǎn)程連接redis的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05Go語言操作RediSearch進(jìn)行搜索方法示例詳解
這篇文章主要為大家介紹了Go語言操作RediSearch進(jìn)行搜索方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Redis消息隊(duì)列的三種實(shí)現(xiàn)方式
本文主要介紹了Redis消息隊(duì)列的三種實(shí)現(xiàn)方式,主要包括List實(shí)現(xiàn)消息隊(duì)列,PubSub消息隊(duì)列,Stream消息隊(duì)列,具有一定的參考價值,感興趣的可以了解一下2023-12-12基于redis 7.2.3的makefile源碼解讀學(xué)習(xí)
這篇文章主要為大家介紹了基于redis 7.2.3的makefile源碼解讀學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12redis事務(wù)_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了redis事務(wù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08