• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

JedisPool资源池优化

技术杂谈 勤劳的小蚂蚁 3个月前 (01-14) 81次浏览 已收录 0个评论 扫描二维码

摘要: 合理的JedisPool资源池参数设置能为业务使用Redis保驾护航,本文将对JedisPool的使用、资源池的参数进行详细说明,最后给出“最合理”配置。

背景

合理的JedisPool资源池参数设置能为业务使用Redis保驾护航,本文将对JedisPool的使用、资源池的参数进行详细说明,最后给出“最合理”配置。

一、使用方法

以官方的2.9.0为例子(Jedis Release),Maven依赖如下:
  1. <dependency>
  2.    <groupId>redis.clients</groupId>
  3.    <artifactId>jedis</artifactId>
  4.    <version>2.9.0</version>
  5.    <scope>compile</scope>
  6. </dependency>
Jedis使用apache commons-pool2对Jedis资源池进行管理,所以在定义JedisPool时一个很重要的参数就是资源池GenericObjectPoolConfig,使用方式如下,其中有很多资源管理和使用的参数(具体看第二节)
注意:后面会提到建议用JedisPoolConfig代替GenericObjectPoolConfig
  1. GenericObjectPoolConfig jedisPoolConfig =newGenericObjectPoolConfig();
  2. jedisPoolConfig.setMaxTotal(..);
  3. jedisPoolConfig.setMaxIdle(..);
  4. jedisPoolConfig.setMinIdle(..);
  5. jedisPoolConfig.setMaxWaitMillis(..);
  6. ...
JedisPool的初始化如下:
  1. // redisHost和redisPort是实例的IP和端口
  2. // redisPassword是实例的密码
  3. // timeout,这里既是连接超时又是读写超时,从Jedis 2.8开始有区分connectionTimeout和soTimeout的构造函数
  4. JedisPool jedisPool =newJedisPool(jedisPoolConfig, redisHost, redisPort, timeout, redisPassword);
  5. 执行命令如下:
  6. Jedis jedis =null;
  7. try{
  8.    jedis = jedisPool.getResource();
  9.    //具体的命令
  10.    jedis.executeCommand()
  11. }catch(Exception e){
  12.    logger.error(e.getMessage(), e);
  13. }finally{
  14.    //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
  15.    if(jedis !=null)
  16.        jedis.close();
  17. }

二、参数说明

JedisPool保证资源在一个可控范围内,并且提供了线程安全,但是一个合理的GenericObjectPoolConfig配置能为应用使用Redis保驾护航,下面将对它的一些重要参数进行说明和建议:
在当前环境下,Jedis连接就是资源,JedisPool管理的就是Jedis连接。

1. 资源设置和使用

空闲Jedis对象检测,下面四个参数组合来完成,testWhileIdle是该功能的开关。

2.空闲资源监测

public为了方便使用,Jedis提供了JedisPoolConfig,它本身继承了GenericObjectPoolConfig设置了一些空闲监测设置
  1. classJedisPoolConfigextendsGenericObjectPoolConfig{
  2.  publicJedisPoolConfig(){
  3.    // defaults to make your life with connection pool easier :)
  4.    setTestWhileIdle(true);
  5.    //
  6.    setMinEvictableIdleTimeMillis(60000);
  7.    //
  8.    setTimeBetweenEvictionRunsMillis(30000);
  9.    setNumTestsPerEvictionRun(-1);
  10.    }
  11. }
所有默认值可以从org.apache.commons.pool2.impl.BaseObjectPoolConfig中看到。

三、资源池大小(maxTotal)、空闲(maxIdle minIdle)设置建议

1.maxTotal:最大连接数

实际上这个是一个很难回答的问题,考虑的因素比较多:
  • 业务希望Redis并发量
  • 客户端执行命令时间
  • Redis资源:例如 nodes(例如应用个数) * maxTotal 是不能超过redis的最大连接数。
  • 资源开销:例如虽然希望控制空闲连接,但是不希望因为连接池的频繁释放创建连接造成不必靠开销。
以一个例子说明,假设:
  • 一次命令时间(borrow|return resource + Jedis执行命令(含网络) )的平均耗时约为1ms,一个连接的QPS大约是1000
  • 业务期望的QPS是50000
那么理论上需要的资源池大小是50000 / 1000 = 50个。但事实上这是个理论值,还要考虑到要比理论值预留一些资源,通常来讲maxTotal可以比理论值大一些。
但这个值不是越大越好,一方面连接太多占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,一个大命令的阻塞即使设置再大资源池仍然会无济于事。

2. maxIdle minIdle

maxIdle实际上才是业务需要的最大连接数,maxTotal是为了给出余量,所以maxIdle不要设置过小,否则会有new Jedis(新连接)开销,而minIdle是为了控制空闲资源监测。
连接池的最佳性能是maxTotal = maxIdle ,这样就避免连接池伸缩带来的性能干扰。但是如果并发量不大或者maxTotal设置过高,会导致不必要的连接资源浪费。 可以根据实际总OPS和调用redis客户端的规模整体评估每个节点所使用的连接池。

3.监控

实际上最靠谱的值是通过监控来得到“最佳值”的,可以考虑通过一些手段(例如jmx)实现监控,找到合理值。

四、常见问题

1.资源“不足”

  1. redis.clients.jedis.exceptions.JedisConnectionException:Couldnotget a resource from the pool
  2. Causedby: java.util.NoSuchElementException:Timeout waiting for idle object
  3. at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
或者
  1. redis.clients.jedis.exceptions.JedisConnectionException:Couldnotget a resource from the pool
  2. Causedby: java.util.NoSuchElementException:Pool exhausted
  3. at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)
两种情况均属于无法从资源池获取到资源,但第一种是超时,第二种是因为blockWhenExhausted为false根本就不等。
遇到此类异常,不要盲目的认为资源池不够大,第三节已经进行了分析。具体原因可以排查:网络、资源池参数设置、资源池监控(如果对jmx监控)、代码(例如没执行jedis.close())、慢查询、DNS等问题。
具体可以参考该文章:https://www.atatech.org/articles/77799

2. 预热JedisPool

由于一些原因(例如超时时间设置较小原因),有的项目在启动成功后会出现超时。JedisPool定义最大资源数、最小空闲资源数时,不会真的把Jedis连接放到池子里,第一次使用时,池子没有资源使用,会new Jedis,使用后放到池子里,可能会有一定的时间开销,所以也可以考虑在JedisPool定义后,为JedisPool提前进行预热,例如以最小空闲数量为预热数量
  1. List<Jedis> minIdleJedisList =newArrayList<Jedis>(jedisPoolConfig.getMinIdle());
  2. for(int i =0; i < jedisPoolConfig.getMinIdle(); i++){
  3.    Jedis jedis =null;
  4.    try{
  5.        jedis = pool.getResource();
  6.        minIdleJedisList.add(jedis);
  7.        jedis.ping();
  8.    }catch(Exception e){
  9.        logger.error(e.getMessage(), e);
  10.    }finally{
  11.    }
  12. }
  13. for(int i =0; i < jedisPoolConfig.getMinIdle(); i++){
  14.    Jedis jedis =null;
  15.    try{
  16.        jedis = minIdleJedisList.get(i);
  17.        jedis.close();
  18.    }catch(Exception e){
  19.        logger.error(e.getMessage(), e);
  20.    }finally{
  21.    }
  22. }


丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:JedisPool资源池优化
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

您必须 登录 才能发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00