Analysis of common exceptions of Redis client

During the use of Redis client, whether the client is improperly used or the Redis server has problems, the client will respond to some exceptions. The following is an analysis of the common exceptions in the use of Jedis:

1, Unable to get connection from connection pool

The number of Jedis objects in the JedisPool is limited. The default is 8. Assuming the default configuration used here, if 8 Jedis objects are occupied and not returned, if the caller wants to borrow Jedis from the JedisPool, it needs to wait (for example, maxWaitMillis > 0). If the Jedis object cannot be obtained within the maxWaitMillis time, the following exception will be thrown.

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)

In another case, if blockWhenExhausted=false is set, the caller will immediately throw an exception without waiting when he finds that there are no resources in the pool. The following exception is the effect when 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)

For this problem, we need to focus on why the connection pool has no resources. There are many possible reasons for the lack of resources

  1. Client: the connection pool setting of high parallel delivery is too small, and the supply exceeds the demand, so the above error will occur. However, under normal circumstances, it only needs to be more than the default maximum number of connections (8), because the processing efficiency of JedisPool and Jedis is is high enough under normal circumstances.
  2. Client: the connection pool is not used correctly, for example, it is not released, as shown in the following code:

Define JedisPool and use the default connection pool configuration.

GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
//Borrowed 8 connections from JedisPool, but did not return them.
for (int i = 0; i < 8; i++) {
    Jedis jedis = null;
    try {
	jedis = jedisPool.getResource();
	jedis.ping();
    } catch (Exception e) {
	e.printStackTrace();
    }
}

When the caller borrows Jedis from the connection pool again (as follows), an exception will be thrown:

jedisPool.getResource().ping();
  1. Client: there are slow query operations. The return speed of Jedis objects held by these slow queries will be relatively slow, causing the pool to be full.
  2. Server: the client is normal, but the Redis server blocks the client command execution process for some reasons, which will also cause the client to throw this exception.

It can be seen that there are many reasons for this exception. Don't be confused by the appearance of the exception. Moreover, there is no master key to solve all problems. Development and operation and maintenance can only continuously strengthen the understanding of Redis and gradually find the problem.

2, Client read / write timeout

When Jedis calls Redis, if there is a read-write timeout, the following exception will appear:

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out

There are also several reasons for this exception:

  • The read-write timeout setting is too short.
  • The command itself is slow.
  • The client and server networks are abnormal.
  • Redis itself is blocked.

3, Client connection timeout

When Jedis calls Redis, if there is a read-write timeout, the following exception will appear:

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out

There are also several reasons for this exception:

  • The connection timeout setting is too short.
  • Redis is blocked, causing the TCP backlog to be full, resulting in a new connection failure.
  • The client and server networks are abnormal.

4, Client buffer exception

When Jedis calls Redis, if the client data flow exception occurs, the following exception will appear.

redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream.

There may be several reasons for this exception:

  • Output buffer full. For example, if the output buffer of an ordinary client is set to 1m 60, this exception will occur if you use the get command to obtain a bigkey (such as 3M).
config set client-output-buffer-limit "normal 1048576 1048576 60 slave 268435456 67108864 60 pubsub 33554432 8388608 60"
  • If a long-time idle connection is actively disconnected by the server, you can query the setting of timeout configuration and whether its own connection pool configuration needs idle detection.
  • Abnormal concurrent read / write: the Jedis object is operated concurrently by multiple threads at the same time, and the above exception may occur.

5, Lua script is executing

If Redis is currently executing Lua script and exceeds the Lua time limit, Jedis will receive the following exception when calling Redis. How to deal with such problems

redis.clients.jedis.exceptions.JedisDataException: BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

6, Redis is loading persistent files

When Jedis calls Redis, if Redis is loading a persistent file, it will receive the following exception.

redis.clients.jedis.exceptions.JedisDataException: LOADING Redis is loading the dataset in memory

7, Redis uses more memory than maxmemory configuration

When Jedis calls Redis to perform a write operation, if Redis's used memory is greater than the maxmemory setting, the following exception will be received. At this time, adjust maxmemory and find the cause of memory growth

redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.

8, The number of client connections is too large

If the number of client connections exceeds maxclients, the newly applied connection will show the following exceptions:

redis.clients.jedis.exceptions.JedisDataException: ERR max number of clients reached

At this time, the new client connection executes any command, and the returned results are as follows:

127.0.0.1:6379> get hello
(error) ERR max number of clients reached

This problem may be difficult because Redis command cannot be executed at this time. Generally speaking, we can start from two aspects.

  1. Client: if the maxclients parameter is not very small, the number of client connections of the application will not exceed maxclients. Generally speaking, it is caused by the improper use of Redis client by the application. At this time, if the application is in a distributed structure, the number of Redis connections can be reduced first by offline some application nodes (for example, nodes occupying more connections). So that most nodes can run normally. At this time, fix the problem by finding program bug s or adjusting maxclients.
  2. Server: if the client is unable to handle it at this time and the current Redis is in high availability mode (such as Redis Sentinel and Redis Cluster), you can consider failover the current Redis.

There is no definite solution to this problem, but no matter which way to deal with it, the rapid recovery of the fault is very important. Of course, it is more important to find the problem, otherwise the number of client connections will still exceed maxclients after a period of time.

reference material:

  1. Redis3 development, operation and maintenance best practices

Keywords: Java Redis Cache

Added by drucifer on Mon, 03 Jan 2022 01:21:01 +0200