How to create Pipeline in RedisCluster
Today I am going to share my experience with Spring-data-redis + Redis 3.2 with cluster. In one of the project we are using spring-data-redis with Redis 3.2 cluster and facing slow performance issue on production after deployment of our changes.
After Analysis, I have find out that in one of the functionality we are sending HMGET redis command for multiple keys at a time and also found that Jedis library does not supports the pipeline for Redis 3.2 cluster. So we analyzed lots of things, like implement pipeline by our self or use Multi Threading or use Async IO Redis Clinet e.g. Redisson.
I am going to explain implementation of pipeline for Redis 3.2 using Jedis library.
After Analysis, I have find out that in one of the functionality we are sending HMGET redis command for multiple keys at a time and also found that Jedis library does not supports the pipeline for Redis 3.2 cluster. So we analyzed lots of things, like implement pipeline by our self or use Multi Threading or use Async IO Redis Clinet e.g. Redisson.
I am going to explain implementation of pipeline for Redis 3.2 using Jedis library.
In the above example, I have redis cluster with three Nodes like
Node 1, Node 2 and Node 3
Each Node having following Slot range
Node 1: 0-115
Node 2: 116-315
Node 3: 316-415
Now You can also see Keys stored in each Node respective to
slot like
Slot No. à Keys
111 à K1, K2, K5
215 à K6, K8, K10
333 à K11, K7, K9
Now we require to call Redis Command HMGET operation with
all keys to get fields value in single request. If we use spring-data-redis + Jedis then we can not able to call pipeline to
get better performance. So for that we tried to implement pipeline by our way.
But for that we require Jedis Native connection from Jedis pool, because
JedisClusterConnection doesn’t provides the pipeline. So we planned by
following way.
1.
First, we grouped each slots respective to Keys
by following code.
Map<Integer, String>
slotKeysMap = new HashMap<>();
for (String key : keys) {
String hKey = RedisCollections.generateKey(locale, collectionName, key);
int slot = JedisClusterCRC16.getSlot(hKey);
if (!slotKeysMap.containsKey(slot)) {
slotKeysMap.put(slot, key);
} else {
key = slotKeysMap.get(slot).concat("|").concat(key);
slotKeysMap.put(slot, key);
}
}
for (String key : keys) {
String hKey = RedisCollections.generateKey(locale, collectionName, key);
int slot = JedisClusterCRC16.getSlot(hKey);
if (!slotKeysMap.containsKey(slot)) {
slotKeysMap.put(slot, key);
} else {
key = slotKeysMap.get(slot).concat("|").concat(key);
slotKeysMap.put(slot, key);
}
}
2.
Second, we took the JedisClusterConnection
object reference.
3.
Third, if we want to call pipeline on
JedisCluster then it is not Possible. So what we did we tried to take Jedis
Native Connection from JedisPool. But JedisCluster also not providing feature
to get Jedis connection from JedisPool for each slot. So to get JedisSlotConnectionHandler/
JedisConnectionHandler we have copied BinaryJedisCluster
class with same package name in our application and this class is part of Jedis
library with pakage name redis.clients.jedis .
// Please find following code which is implemented by me:
JedisClusterConnection jedisClusterConnection = (JedisClusterConnection) stringRedisTemplate.getConnectionFactory().getClusterConnection(); JedisCluster jedisCluster = jedisClusterConnection.getNativeConnection(); JedisSlotBasedConnectionHandler jedisClusterConnectionHandler = jedisCluster.getConnectionHandler(); // This method is not part of actual Jedis library but it is part of copied BinaryJedisCluste.java class in our project
// Method we have
included into copied class is:
public JedisSlotBasedConnectionHandler getConnectionHandler() { return (JedisSlotBasedConnectionHandler) this.connectionHandler; }
Now we can able to get connectionHandler to get the Jedis
Native Connection from JedisPool.
You may face Issue at runtime:
By this way we can implement pipeline but the actual problem
is this solution is not generic, in future if jedis library changed then again
we have to copy that class changes into our project and we have to repackage
and deploy our application.
Another problem is sometime application server
class loader loads the Jedis Library class so we will get the Runtime exception
of NoSuchMethodError.
à
So what I did, I copied source code into our local environment and added the
method to get connection handler and tested library in our local environment.
It is working fine.
I also raised the PR to include this method in Jedis
Library. So every one can use Pipeline by getting Jedis connection from Jedis
Pool. Please check from following URL: https://github.com/xetorthio/jedis/pull/1532
Comments
Post a Comment