Spring Data Redis: Redis Pipeline returning always

2019-07-19 02:27发布

问题:

I would like to retrieve multiple hashmap values with only specified fields. So I opted-in to Redis pipeline.

While testing the below code, i see redisResponse1 is always null, where as redisResponse2 has value.

    getRedisTemplate().executePipelined(new RedisCallback<Object>() { 
        @Override
        public Object doInRedis(RedisConnection connection) throws DataAccessException {
                List<byte[]> redisResponse1 = connection.hMGet(key.getBytes(), params);
                List<byte[]> redisResponse2 = getRedisTemplate().getConnectionFactory().getConnection().hMGet(key.getBytes(), specificParams);
                return null;
        }
    });

When I look into the code and found that below, where

a) redisResponse2 is not executed with pipeline option

b) redisResponse1 is executed with pipeline (isPipelined() == true) but returning always null.

public List<byte[]> hMGet(byte[] key, byte[]... fields) {
    try {
        if (isPipelined()) {
            pipeline(new JedisResult(pipeline.hmget(key, fields)));
            return null;
        }
        if (isQueueing()) {
            transaction(new JedisResult(transaction.hmget(key, fields)));
            return null;
        }
        return jedis.hmget(key, fields);
    } catch (Exception ex) {
        throw convertJedisAccessException(ex);
    }
}

So questions are

1) How do I achieve my use case with pipeline option?

2) What is the impact accessing getRedisTemplate().getConnectionFactory().getConnection() within this RedisCallback?

3) How this whole pipeline concept is working? Is it like dynamic Lua? where this Java code is converted as Lua script and send to Redis as script, executed in Redis and come back? Surprised on within this callback; the code is accessing/updating the outer class variables as well, so what will happen to all that variables? All those outer class variables also send to redis in lua?

4) I see many examples about doInRedis API is returning null; Why so? How to return/get valid Object from that?

回答1:

The majority of your questions are available within the Spring Data Redis reference documentation.

Before digging into Pipelining, a single multi-get from one Hash does not require Pipelining because it's only a single command. Pipelining won't improve performance/stability/… of your Redis interaction.

Pipelining is arranged as callback and intended to issue multiple commands without awaiting the result immediately – think of it as a batch where you get all results later. Because pipelining synchronizes responses at the very end, you don't receive result values within the callback but at the very end, when the pipelining session is synchronized and executePipelined(…) terminates.

Your code should rather look like:

List<Object> results = getRedisTemplate().executePipelined(new RedisCallback<Object>() {

    @Override
    public Object doInRedis(RedisConnection connection) {

            connection.hMGet(key.getBytes(), params);

            return null;
    }
});

List<Object> hmget = (List<Object>) results.get(0);

You have to use only the connection that you receive as callback argument because the connection has entered pipelining mode. Obtaining a connection from outside the callback (like template.getConnectionFactory().getConnection()) will open a new connection and execute Redis commands with awaiting responses – no pipelining is applied to any external obtained connection.

You can also use methods of RedisTemplate instead of working with the plain connection. executePipelined(…) binds the connection used in the callback to the current Thread and reuses that bound connection if you call template API methods.

Regarding your Lua question: The code/method calls are not transposed to Lua.