使用jedis如何写入redis集群中的特定插槽/节点

时间:2018-03-08 18:09:19

标签: redis jedis redis-sentinel redis-cluster

我正在尝试提高向redis群集写入数据的性能。 我们计划从redi-sentinel迁移到集群模式以实现可伸缩性。

但是,与redis-sentinel相比,写操作的性能非常低。我们在redis-sentinel中利用了管道,但集群模式不支持管道。

所以,我正在考虑将所有转到同一节点的密钥分组,然后使用管道将批处理发送到该特定节点。

所以,我想知道如何知道/计算(在写入群集之前)将特定密钥写入哪个节点/槽?

2 个答案:

答案 0 :(得分:1)

解决方案1:
找到一种解决方案来识别密钥将进入的插槽。 JedisCluster有一些API可以实现它。

int slotNum = JedisClusterCRC16.getSlot(key); - 提供密钥的插槽号。

Set<HostAndPort> redisClusterNode = new HashSet<HostAndPort>();
redisClusterNode.add(new HostAndPort(hostItem, port));

JedisSlotBasedConnectionHandler connHandler = new 
              JedisSlotBasedConnectionHandler(redisClusterNode, poolConfig, 60);

Jedis jedis = connHandler.getConnectionFromSlot(slotNum);

这为集群中的特定节点提供了jedis对象(来自内部的Jedispool)。
现在使用上面的jedis对象,可以轻松地为特定节点(在集群中)管道化所有命令

Pipeline pipeline = jedis.pipelined();
pipeline.multi();
for(Entry<String, Map<String, String>> kvf : kvfs.entrySet()) {
   pipeline.hmset(kvf.getKey(), kvf.getValue());
}
pipeline.exec();

尽管这种方法(使用JedisCluster)给出了密钥所针对的适当节点,但这并没有为我提供预期的性能,我认为这是由于知道插槽号和节点(插槽)所涉及的过程。
每次我们尝试获取包含插槽号的实际节点(jedis)时,上面的过程似乎建立了与节点(在集群中)的物理连接。因此,这会妨碍我们拥有数百万个密钥的性能。
因此,使用Lettuce包的另一种方法(下面)帮助我完成了这个。

解决方案2:
使用的Lettuce包支持在集群模式下发送批量命令。

     <groupId>biz.paluch.redis</groupId>
     <artifactId>lettuce</artifactId>
     <version>4.4.3.Final</version>

代码段:

RedisClusterClient client = RedisClusterClient.create(RedisURI.create("hostname", "port"));
StatefulRedisClusterConnection<String, String> connection = client.connect();

RedisAdvancedClusterAsyncCommands<String, String> commands = connection.async();
// Disabling auto-flushing
commands.setAutoFlushCommands(false);

List<RedisFuture<?>> futures = new ArrayList<>();
// kvf is of type Map<String, Map<String, String>>
for (Entry<> e : kvf.entrySet())
{
   futures.add(commands.hmset( (String) e.getKey(), (Map<String, String>) e.getValue()));
}
// write all commands to the transport layer
commands.flushCommands();
// synchronization example: Wait until all futures complete
LettuceFutures.awaitAll(10, TimeUnit.SECONDS,
futures.toArray(new RedisFuture[futures.size()]));

参考:https://github.com/lettuce-io/lettuce-core/wiki/Pipelining-and-command-flushing

答案 1 :(得分:0)

  

但群集模式不支持管道

<强> WRONG!

使用单个管道,您只能将多个命令发送到同一节点的同一连接。它与此节点是单个实例还是Redis Cluster的成员无关。

所以你的问题应该是 使用单个管道,我们不能发送多个命令,其中密钥分布在多个插槽上。为了解决这个问题,您希望这些密钥位于同一个插槽中。我们如何实现这一目标?

  

如何知道/计算(在写入群集之前)将特定密钥写入哪个节点/槽

你不需要自己做数学。您可以使用Hash Tags强制多个密钥成为同一散列槽的一部分。

因此,您只需要使用相同的Hash Tags重命名您希望位于同一插槽中的密钥。例如将user-nameuser-age重命名为{user-id}user-name{user-id}user-age

有关详细信息,请参阅Hash Tags doc

相关问题