You can set JedisPool parameters to proper values to improve Redis performance. This topic describes how to use JedisPool and configure the resource pool parameters. This topic also describes the recommended settings to optimize JedisPool.

Use JedisPool

Jedis 2.9.0 is used in this example. The following sample code shows the Maven dependency:

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

Jedis manages the resource pool by using Apache Commons-pool2. When you define JedisPool, we recommend that you pay attention to the GenericObjectPoolConfig parameter of the resource pool. The following sample code shows how to use this parameter.

GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig();
jedisPoolConfig.setMaxTotal(...);
jedisPoolConfig.setMaxIdle(...);
jedisPoolConfig.setMinIdle(...);
jedisPoolConfig.setMaxWaitMillis(...);
...

The following example shows how to initialize JedisPool:

//redisHost specifies the IP address of the instance. redisPort specifies the port of the instance. redisPassword specifies the password of the instance. The timeout parameter specifies the connection timeout and the read/write timeout.
JedisPool jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort, timeout, redisPassword//);
//Run the following command:
Jedis jedis = null;
try {
    jedis = jedisPool.getResource();
    //Specific commands
    jedis.executeCommand()
} catch (Exception e) {
    logger.error(e.getMessage(), e);
} finally {
    //In JedisPool mode, the Jedis resource is returned to the resource pool.
    if (jedis != null) 
        jedis.close();
}

Parameters

The Jedis connection is a resource managed by JedisPool in the connection pool. JedisPool is a thread-safe pool of connections. It allows you to keep all resources within a manageable range. If you set the GenericObjectPoolConfig parameter to a proper value, you can improve the performance of Redis and reduce resource consumption. The following two tables describe important parameters and provide the recommended settings.

Table 1. Parameters related to resource settings and resource usage
Parameter Description Default value Recommended settings
maxTotal The maximum number of connections that are supported by the pool. 8 For more information, see Recommended settings.
maxIdle The maximum number of idle connections in the pool. 8 For more information, see Recommended settings.
minIdle The minimum number of idle connections in the pool. 0 For more information, see Recommended settings.
blockWhenExhausted Specifies whether the client must wait when the resource pool is exhausted. Only when this parameter is set to true, the maxWaitMillis parameter takes effect. true We recommend that you use the default value.
maxWaitMillis The maximum number of milliseconds that the client must wait when no connection is available. A value of -1 specifies that the connection never times out. We recommend that you do not use the default value.
testOnBorrow Specifies whether to validate connections by using the PING command before the connections are borrowed from the pool. Invalid connections are removed from the pool. false We recommend that you set this parameter to false when the workload is heavy. This allows you to reduce the overhead of a ping test.
testOnReturn Specifies whether to validate connections by using the PING command before the connections are returned to the pool. Invalid connections are removed from the pool. false We recommend that you set this parameter to false when the workload is heavy. This allows you to reduce the overhead of a ping test.
jmxEnabled Specifies whether to enable Java Management Extensions (JMX) monitoring. true We recommend that you enable JMX monitoring. Take note that you must also enable the feature for your application.

Idle Jedis object detection provides the following four parameters.

Table 2. Parameters related to idle resource detection
Parameter Description Default value Recommended settings
testWhileIdle Specifies whether to validate connections by running the PING command during the process of idle resource detection. Invalid connections are evicted. false true
timeBetweenEvictionRunsMillis Specifies the cycle of idle resources detection. Unit: milliseconds. A value of -1 specifies idle resource detection is disabled. We recommend that you set this parameter to a proper value. You can also use the default configuration in JedisPoolConfig.
minEvictableIdleTimeMillis The minimum idle time of a resource in the resource pool. Unit: milliseconds. When the upper limit is reached, the idle resource is evicted. 180000 (3 minutes) The default value is suitable for most cases. You can also use the configuration in JeidsPoolConfig based on your business requirements.
numTestsPerEvictionRun The number of resources to be detected within each cycle. 3 You can change the value based on your application connections. A value of -1 specifies that the system checks all connections for idle resources.

Jedis provides JedisPoolConfig that uses some configurations of GenericObjectPoolConfig for idle resource detection.

public class JedisPoolConfig extends GenericObjectPoolConfig {
  public JedisPoolConfig() {
    // defaults to make your life with connection pool easier :)
    setTestWhileIdle(true);
    //
    setMinEvictableIdleTimeMillis(60000);
    //
    setTimeBetweenEvictionRunsMillis(30000);
    setNumTestsPerEvictionRun(-1);
    }
}
Note You can view all default values in org.apache.commons.pool2.impl.BaseObjectPoolConfig.

Recommended settings

maxTotal: The maximum number of connections.

To set a proper value of maxTotal, take note of the following factors:

  • The expected concurrent connections based on your business requirements.
  • The amount of time that is consumed by the client to run the command.
  • The limit of Redis resources. For example, if you multiply maxTotal by the number of nodes (ECS instances), the product must be smaller than the supported maximum number of connections in Redis. You can view the maximum connections on the Instance Information page in the ApsaraDB for Redis console.
  • The resource that is consumed to create and release connections. If the number of connections that are created and released for a request is large, the processes that are performed to create and release connections are adversely affected.

For example, the average time that is consumed to run a command, or the average time that is required to borrow or return resources and to run Jedis commands with network overhead, is approximately 1 ms. The queries per second (QPS) of a connection is about 1 second/1 millisecond = 1000. The expected QPS of an individual Redis instance is 50,000 (the total number of QPS divided by the number of Redis shards). The theoretically required size of a resource pool (maxTotal) is 50,000/1,000 = 50.

However, this is only a theoretical value. To reserve some resources, the value of the maxTotal parameter can be larger than the theoretical value. However, if the value of the maxTotal parameter is too large, the connections consume a large amount of client and server resources. For Redis servers that have a high QPS, if a large number of commands are blocked, the issue cannot be solved even by a large resource pool.

maxIdle and minIdle

maxIdle is the actual maximum number of connections required by workloads. maxTotal includes the number of idle connections as a surplus. If the value of maxIdle is too small on heavily loaded systems, new Jedis connections are created to serve the requests. minIdle specifies the minimum number of established connections that must be kept in the pool.

The connection pool achieves its best performance when maxTotal = maxIdle. This way, the performance is not affected by the scaling of the connection pool. We recommend that you set the maxIdle and minIdle parameters to the same value if the user traffic fluctuates. If the number of concurrent connections is small or the value of the maxIdle parameter is too large, the connection resources are wasted.

You can evaluate the size of the connection pool used by each node based on the actual total QPS and the number of clients that Redis serves.

Retrieve proper values based on monitoring data

In actual scenarios, a more reliable method is to try to retrieve optimal values based on monitoring data. You can use JMX monitoring or other monitoring tools to find proper values.

FAQ

Insufficient resources

You cannot obtain resources from the resource pool in the following cases:

  • Timeout:
    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)
  • When you set the blockWhenExhausted parameter to false, the time specified by borrowMaxWaitMillis is not used and the borrowObject call blocks the connection until an idle connection is available.

    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)

This exception may not be caused by a limited pool size. For more information, see Recommended settings. To fix this issue, we recommend that you check the network, the parameters of the resource pool, the resource pool monitoring (JMX monitoring), the code (for example, the reason is that jedis.close() is not executed), slow queries, and the domain name system (DNS).

Preload JedisPool

If you specify a small timeout value, the project may time out after it is started. JedisPool does not create a Jedis connection in the connection pool when JedisPool defines the maximum number of resources and the minimum number of idle resources. If no idle connection exists in the pool, a new Jedis connection is created. This connection is released to the pool after the connection is used. However, the process in which you create a connection and repeatedly release the connection may take a long period of time. Therefore, we recommend that you preload JedisPool with the minimum number of idle connections after JedisPool is defined. The following example shows how to preload 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 {
    }
}

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 {
    
    }
}