使用jedis如何写入redis集群中的特定slot/node
Using jedis how to write to a specific slot/node in redis cluster
我正在尝试提高向redis 集群写入数据的性能。
我们计划从 redi-sentinel 转移到集群模式以实现可扩展性。
但是,与redis-sentinel相比,写操作的性能非常差。我们在 redis-sentinel 中利用了管道,但集群模式不支持管道。
所以,我想将所有转到同一节点的键分组,然后使用管道将批处理发送到该特定节点。
所以,我想知道如何 know/compute(在写入集群之前)将 node/slot 写入特定密钥?
but cluster mode doesn't support pipeline
错误!
使用单个管道,您只能将多个命令发送到同一节点的同一连接。与该节点是单个实例还是Redis Cluster
.
的成员无关
所以你的问题应该是使用单个管道,我们不能发送多个命令,密钥分布在多个槽上。为了解决这个问题,您希望这些键位于同一个插槽中。我们怎样才能做到这一点?
how to know/compute (before writing to cluster) to which node/slot a particular key would be written to
您不需要自己计算。您可以使用 Hash Tags
强制多个键成为同一哈希槽的一部分。
所以你只需要重命名那些你想位于同一个插槽中的键,具有相同的Hash Tags
。例如将 user-name
和 user-age
重命名为 {user-id}user-name
和 {user-id}user-age
详情见Hash Tags
doc。
解决方案一:
找到了一个解决方案来识别钥匙将进入的插槽。 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) 时,上述过程似乎都建立了到节点(在集群中)的物理连接。因此,这会影响我们拥有数百万个密钥的性能 in-case。
所以,另一种使用生菜包的方法(如下)帮助我克服了这个问题。
方案二:
使用了 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
我正在尝试提高向redis 集群写入数据的性能。 我们计划从 redi-sentinel 转移到集群模式以实现可扩展性。
但是,与redis-sentinel相比,写操作的性能非常差。我们在 redis-sentinel 中利用了管道,但集群模式不支持管道。
所以,我想将所有转到同一节点的键分组,然后使用管道将批处理发送到该特定节点。
所以,我想知道如何 know/compute(在写入集群之前)将 node/slot 写入特定密钥?
but cluster mode doesn't support pipeline
错误!
使用单个管道,您只能将多个命令发送到同一节点的同一连接。与该节点是单个实例还是Redis Cluster
.
所以你的问题应该是使用单个管道,我们不能发送多个命令,密钥分布在多个槽上。为了解决这个问题,您希望这些键位于同一个插槽中。我们怎样才能做到这一点?
how to know/compute (before writing to cluster) to which node/slot a particular key would be written to
您不需要自己计算。您可以使用 Hash Tags
强制多个键成为同一哈希槽的一部分。
所以你只需要重命名那些你想位于同一个插槽中的键,具有相同的Hash Tags
。例如将 user-name
和 user-age
重命名为 {user-id}user-name
和 {user-id}user-age
详情见Hash Tags
doc。
解决方案一:
找到了一个解决方案来识别钥匙将进入的插槽。 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) 时,上述过程似乎都建立了到节点(在集群中)的物理连接。因此,这会影响我们拥有数百万个密钥的性能 in-case。
所以,另一种使用生菜包的方法(如下)帮助我克服了这个问题。
方案二:
使用了 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