Jedis是什么
jedis就是基于java語言的redis客戶端批狱,集成了redis的命令操作态坦,提供了連接池管理廉邑。
redis-cli是redis官方提供的客戶端音婶,可以看作一個shell程序慨畸,它可以發(fā)送命令對redis進行操作。
對于jedis同理是使用java語言操作redis衣式,雙方都遵循redis提供的協(xié)議寸士,按照協(xié)議開發(fā)對應(yīng)的客戶端。
Maven依賴
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
</dependency>
Jedis直連
jedis直連碴卧,本質(zhì)是定義一個tcp連接弱卡,然后使用socket技術(shù)進行通信
//1.生成一個jedis對象,這個對象負責和指定Redis節(jié)點進行通信
Jedis jedis = new Jedis("119.23.226.29", 6379);
//帶密碼需要執(zhí)行認證方法
//jedis.auth("123456");
//2.jedis執(zhí)行set操作
jedis.set("hello", "world");
//3.jedis執(zhí)行g(shù)et操作住册,value="world"
String value = jedis.get("hello");
構(gòu)造方法參數(shù)介紹
Jedis簡單使用
字符串
// 1.string
//輸出結(jié)果: OK
jedis.set("hello", "world");
//輸出結(jié)果: world
jedis.get("hello");
//輸出結(jié)果:1
jedis.incr("counter");
哈希
// 2.hash
jedis.hset("myhash", "f1", "v1");
jedis.hset("myhash", "f2", "v2");
//輸出結(jié)果 : {f1=v1, f2=v2}
jedis.hgetAll("myhash");
列表
// 3.list
jedis.rpush("mylist", "1");
jedis.rpush("mylist", "2");
jedis.rpush(" mylist", "3");
//輸出結(jié)果 : [1, 2, 3]
jedis.lrange("mylist", 0, -1);
集合
// 4.set
jedis.sadd(" myset", "a");
jedis.sadd(" myset", "b");
jedis.sadd(" myset", "a");
//輸出結(jié)果 : [b, a]
jedis.smembers("myset");
有序集合
// 5.zset
jedis.zadd("myzset", 99, "tom");
jedis.zadd("myzset", 66, "peter");
jedis.zadd("myzset", 33, "james");
//輸出結(jié)果 : [[["james"],33.0], [["peter"],66.0], [["tom"],99.0]]
jedis.zrangeWithScores("myzset", 0, -1);
Jedis連接池
jedis直連
每次操作創(chuàng)建一個jedis對象婶博,執(zhí)行完畢后關(guān)閉連接,對應(yīng)的就是一次Tcp連接界弧。
jedis連接池
預先生成一批jedis連接對象放入連接池中凡蜻,當需要對redis進行操作時從連接池中借用jedis對象搭综,操作完成后歸還。這樣jedis對象可以重復使用划栓,避免了頻繁創(chuàng)建socket連接兑巾,節(jié)省了連接開銷。
方案對比
連接池簡單使用
這里只是對連接池進行一個簡單使用忠荞,實際開發(fā)通常會對JedisPool進行封裝蒋歌,進行一些參數(shù)配置和方法定義等,在使用Jedis API時委煤,也會對常用API進行封裝堂油,方便程序調(diào)用
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class Demo {
public static void main(String[] args) {
//連接池配置對象,包含了很多默認配置
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
//初始化Jedis連接池,通常來講JedisPool是單例的
JedisPool jedisPool = new JedisPool(poolConfig, "119.23.226.29", 6379);
Jedis jedis = null;
try {
//1.從連接池獲取jedis對象
jedis = jedisPool.getResource();
//2.執(zhí)行操作
jedis.set("hello", "jedis");
System.out.println(jedis.get("hello"));
} catch (Exception e) {
e.printStackTrace();
} finally{
//如果使用JedisPool碧绞,那么close操作不是關(guān)閉連接府框,代表歸還連接池
if(jedis != null){
jedis.close();
}
}
}
}
Jedis配置優(yōu)化
對于企業(yè)級開發(fā)來說,連接池的合理使用是非常重要的讥邻,如果設(shè)置不當會引起很多不必要的麻煩迫靖,容易造成線上的故障。
其實關(guān)于配置是一個比較難或者說沒有確定答案的部分兴使,這里只能給出一些思路和解決一些異常的方法系宜。
連接池重要配置
為了方便使用,Jedis提供了JedisPoolConfig
发魄,它本身繼承了GenericObjectPoolConfig
設(shè)置了一些空閑監(jiān)測設(shè)置
資源數(shù)控制
借還參數(shù)
適合的maxTotal
其實這個參數(shù)是比較難確定的盹牧,舉個例子:
- 命令平均執(zhí)行時間0.1ms = 0.001s
- 業(yè)務(wù)需要50000 QPS
- maxTotal理論值 = 0.001 * 50000 = 50個。實際值要偏大一些
對于適合的maxTotal而言励幼,我們需要考慮
- 業(yè)務(wù)希望Redis并發(fā)量
- 客戶端執(zhí)行命令時間
- Redis資源:例如 nodes(例如應(yīng)用個數(shù)) * maxTotal 是不能超過redis的最大連接數(shù)
- 資源開銷:例如雖然希望控制空閑連接汰寓,但是不希望因為連接池的頻繁釋放創(chuàng)建連接造成不必靠開銷
適合的maxIdle和minIdle
- 建議maxIdle = maxTotal,減少創(chuàng)建新連接的開銷
- 建議預熱minIdle苹粟,減少第一次啟動后的新連接開銷
常見問題
無法從資源池獲取到資源踩寇,原因是獲取空閑連接超時了。
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
…
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
無法從資源池獲取到資源六水,原因是池子中資源已經(jīng)耗盡了。
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
…
Caused by: java.util.NoSuchElementException: Pool exhausted
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)
解決思路
- 慢查詢阻塞:池子連接都被hang住辣卒。
- 資源池參數(shù)不合理:例如QPS高掷贾、池子小。
- 連接泄露(沒有close()):此類問題比較難定位荣茫,例如client list想帅、netstat等,最重要的是寫合理的代碼啡莉。
- DNS異常等港准。
例如連接泄漏
public static void main(String[] args){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10);
jedisPoolConfig.setMaxWaitMillis(1000);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
for(int i = 0; i < 10; i++){
Jedis jedis = null;
try{
jedis = jedisPool.getResource();
jedis.ping();
//沒有進行連接的歸還
}catch(Exception e){
e.printStackTrace();
}
//再次獲取資源就會出錯
jedisPool.getResource().ping();
}
}