Redis进阶-JedisPool参数优化 & 如何合理估算核心参数 & redis连接池预热

在这里插入图片描述


Pre

Redis Version : 5.0.3

maven依赖:jedis 2.9.0

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

JedisPoolConfig & JedisPool

如果我们选择Jedis作为客户端来操作Redis的话, 操作单节点的Redis,JedisPool & JedisPoolConfig 那肯定要好好地了解一番。 合理的JedisPool资源池参数设置能够有效地提升Redis性能。

查看JedisPool的源码,你会看到好多构造方法中都有GenericObjectPoolConfig

在这里插入图片描述

GenericObjectPoolConfig的子类 JedisPoolConfig 我们用的比较多,当然了你也可以直接使用 GenericObjectPoolConfig 来初始化 PoolConfig .

初始化Jedis 如下: 在这里插入图片描述


参数说明

Jedis连接就是连接池中JedisPool管理的资源,JedisPool保证资源在一个可控范围内,并且保障线程安全。使用合理的GenericObjectPoolConfig配置能够提升Redis的服务性能,降低资源开销。

GenericObjectPoolConfig 以及他的父类 BaseObjectPoolConfig 有许多属性可以设置。

BaseObjectPoolConfig 默认值都在这里了。

在这里插入图片描述

资源设置与使用相关参数

在这里插入图片描述

空闲资源检测相关参数

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

在这里插入图片描述

为了方便使用,Jedis提供了JedisPoolConfig,它继承了GenericObjectPoolConfig在空闲检测上的一些设置。

在这里插入图片描述


关键参数设置建议

maxTotal(最大连接数)

想合理设置maxTotal(最大连接数)需要考虑的因素较多,如:

  • 业务希望的Redis并发量;
  • 客户端执行命令时间;

举个例子 Redis资源,例如nodes (如应用个数等) * maxTotal不能超过Redis的最大连接数;

资源开销,例如虽然希望控制空闲连接,但又不希望因为连接池中频繁地释放和创建连接造成不必要的开销。

假设一次命令时间,即borrow|return resource加上Jedis执行命令 ( 含网络耗时)的平均耗时约为1ms,一个连接的QPS大约是1000,而业务期望的QPS是50000,那么理论上需要的资源池大小是50000/ 1000= 50。

但事实上这只是个理论值,除此之外还要预留一些资源,所以maxTotal可以比理论值大一些。这个值不是越大越好,一方面连接太多会占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,如果出现大命令的阻塞,即使设置再大的资源池也无济于事。


maxIdle与minIdle

maxIdle实际上才是业务需要的最大连接数,maxTotal 是为了给出余量,所以 maxIdle 不要设置得过小,否则会有new Jedis(新连接)开销,而minIdle是为了控制空闲资源检测。

连接池的最佳性能是maxTotal=maxIdle,这样就避免了连接池伸缩带来的性能干扰。但如果并发量不大或者maxTotal设置过高,则会导致不必要的连接资源浪费。

可以根据实际总QPS和调用Redis的客户端规模整体评估每个节点所使用的连接池大小。


使用监控获取合理值

在实际环境中,比较可靠的方法是通过监控来尝试获取参数的最佳值。可以考虑通过JMX等方式实现监控,从而找到合理值。


常见问题

资源不足

下面两种情况均属于无法从资源池获取到资源。

  • 超时

    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)
    
  • blockWhenExhausted 为false,因此不会等待资源释放:

    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)
    

此类异常的原因不一定是资源池不够大,请参见关键参数设置建议中的分析。建议从网络、资源池参数设置、资源池监控(如果对JMX监控)、代码(例如没执行jedis.close())、慢查询、DNS等方面进行排查。


预热JedisPool

由于一些原因(如超时时间设置较小等),项目在启动成功后可能会出现超时。或者你预估你的应用启动时会有大量的请求过来。。。.

JedisPool定义最大资源数、最小空闲资源数时,不会在连接池中创建Jedis连接。

初次使用时,池中没有资源使用则会先new Jedis,使用后再放入资源池,该过程会有一定的时间开销,所以建议在定义JedisPool后,以最小空闲数量为基准对JedisPool进行预热.

List<Jedis> minIdleJedisList = new ArrayList<Jedis>(jedisPoolConfig.getMinIdle());
 
for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
    Jedis jedis = null;
    try {
        jedis = pool.getResource();
        minIdleJedisList.add(jedis);
        jedis.ping();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    } finally {
     // 这里啥都不要做,不要close,否则连接池中只会有1个连接

    }
}
 
 
// 统一将预热的连接还回连接池 
for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) {
    Jedis jedis = null;
    try {
        jedis = minIdleJedisList.get(i);
        
        // 将预热的连接还回连接池 
        jedis.close();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    } finally {
 
    }
}

总之,要根据实际系统的QPS和调用redis客户端的规模整体评估每个节点所使用的连接池大小。

要使用JedisPool来管理Redis连接池,可以按照以下步骤操作: 1. 首先,确保你已经将Jedis库添加到你的项目中。你可以在Maven或Gradle的配置文件中添加以下依赖: ```xml <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>版本号</version> </dependency> ``` 2. 在代码中导入JedisJedisPool相关的类: ```java import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; ``` 3. 创建JedisPoolConfig对象,并设置连接池的相关属性(如最大连接数、最大空闲连接数等): ```java JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(100); // 设置最大连接数 jedisPoolConfig.setMaxIdle(10); // 设置最大空闲连接数 // 可以根据需要设置其他属性,比如最小空闲连接数、连接超时时间等 ``` 4. 创建JedisPool对象,传入Redis服务器的主机名、端口号、连接超时时间和密码(如果有的话): ```java JedisPool jedisPool = new JedisPool(jedisPoolConfig, "localhost", 6379, 10000, "password"); ``` 注意:如果Redis服务器没有设置密码,可以省略密码参数。 5. 从连接池中获取Jedis对象,并使用Jedis对象进行Redis操作: ```java try (Jedis jedis = jedisPool.getResource()) { // 执行Redis操作,如存储键值对 jedis.set("key", "value"); String value = jedis.get("key"); System.out.println(value); } catch (Exception e) { // 异常处理 } ``` 6. 当不再需要使用Redis连接时,记得将Jedis对象还回连接池: ```java jedis.close(); ``` 这就是使用JedisPool来管理Redis连接池的基本步骤。通过合理配置连接池的属性,可以提高Redis操作的性能和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值