Redis Cluster
使用Redis Cluster 需要Redis 服务版本3.0以上,Redis Cluster 提供了一套独有的特性和功能。查看Redis 官网以获得更多信息。
只有jedis 和 lettuce 支持Redis Cluster。
1. Enabling Redis Cluster
集群的支持是基于非集群通讯构建的。RedisClusterConnection 是RedisConnection 的一个扩展,用来处理和Redis Cluster的通讯,转换错误信息到Spring DAO异常层。RedisClusterConnection 是通过RedisConnectionFactory 创建的,该工程的创建要依据于RedisClusterConfiguration配置。
Example 1. Sample RedisConnectionFactory Configuration for Redis Cluster
@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class ClusterConfigurationProperties {
/*
* spring.redis.cluster.nodes[0] = 127.0.0.1:7379
* spring.redis.cluster.nodes[1] = 127.0.0.1:7380
* ...
*/
List nodes;
/**
* Get initial collection of known cluster nodes in format {@code host:port}.
*
* @return
*/
public List getNodes() {
return nodes;
}
public void setNodes(List nodes) {
this.nodes = nodes;
}
}
@Configuration
public class AppConfig {
/**
* Type safe representation of application.properties
*/
@Autowired ClusterConfigurationProperties clusterProperties;
public @Bean RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(
new RedisClusterConfiguration(clusterProperties.getNodes()));
}
}
RedisClusterConfiguration can also be defined via PropertySource.
RedisClusterConfiguration 也可以通过PropertySource 来定义。
Configuration Properties
spring.redis.cluster.nodes: Comma delimited list of host:port pairs.
spring.redis.cluster.max-redirects: Number of allowed cluster redirections.
这个初始化的配置为驱动库指定了一组初始化集群节点。集群可以在线修改配置,但修改结果只会保存在本驱动的内存中,不会写入到配置文件中。
2. Working With Redis Cluster Connection
如上面提到的,Redis Cluster的行为和Redis 的单节点不同,甚至和一个Sentinel 监控的主从模式也不一样。主要原因是集群自动分片,将一个key 映射到16384个槽中的一个,这些槽分布在多个节点上。因此操作多个key 的命令必须保证所有的key 都映射到同一个槽上,避免跨槽执行错误。更进一步说,今后一个单独的集群节点,只服务于一组专用的keys,请求一个命令到一个Server,只能得到该Server 上拥有keys 的对应结果。一个非常简单的例子是执行KEYS命令,当发布该命令到集群环境中的某个节点是,只能得到该节点上拥有的keys,而不是集群中所有的keys。所以要得到集群中所有的keys,必须从集群的所有主节点上获取所有的keys。
驱动库会处理将一个特定的keys 重定向到对应的槽节点上,更高级的功能,像跨节点收集信息,或发送命令到集群的所有节点上,都由RedisClusterConnection 来处理。像前面获取所有keys 的例子,需要keys(模式)方法来获取集群中每一个主节点的keys,就是说要执行KEYS 命令在每一个节点上,然后获取结果返回累加的所有keys。其实仅仅需要请求keys 到一个单独节点的RedisClusterConnection 上就可以驱动上面所有的处理(如 keys(node,pattern))。
获取一个RedisClusterNode 有多种方式,可以通过RedisClusterConnection.clusterGetNodes 方法,或通过host 和port构建,或通过Id 构建。
Example 2. Sample of Running Commands Across the Cluster
redis-cli@127.0.0.1:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460
7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-10922
164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-16383
b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected
RedisClusterConnection connection = connectionFactory.getClusterConnnection();
connection.set("foo", value);
connection.set("bar", value);
connection.keys("*");
connection.keys(NODE_7379, "*");
connection.keys(NODE_7380, "*");
connection.keys(NODE_7381, "*");
connection.keys(NODE_7382, "*");
Master node serving slots 0 to 5460 replicated to slave at 7382
Master node serving slots 5461 to 10922
Master node serving slots 10923 to 16383
Slave node holding replicates of master at 7379
Request routed to node at 7381 serving slot 12182
Request routed to node at 7379 serving slot 5061
Request routed to nodes at 7379, 7380, 7381 → [foo, bar]
Request routed to node at 7379 → [bar]
Request routed to node at 7380 → []
Request routed to node at 7381 → [foo]
Request routed to node at 7382 → [bar]
当所有的keys 都映射到同一个slot 时,跨slot 的请求像MGET 会被本地驱动库自动处理。然后一旦不是这种情况,RedisClusterConnection 会执行多个并行的GET 命令到多个节点的slot 上,并返回一个累加的结果。很明显这些没有单个slot 执行性能高,因此使用时要当心。毫无疑问,请考虑将所有的keys 聚集到同一个slot 上,可以通过提供一个前缀,像 {my-prefix}.foo 和 {my-prefix}.bar 一样将映射到同一个slot 上。
Example 3. Sample of Cross Slot Request Handling
redis-cli@127.0.0.1:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460
7bb...
RedisClusterConnection connection = connectionFactory.getClusterConnnection();
connection.set("foo", value); // slot: 12182
connection.set("{foo}.bar", value); // slot: 12182
connection.set("bar", value); // slot: 5461
connection.mGet("foo", "{foo}.bar");
connection.mGet("foo", "bar");
Same Configuration as in the sample before.
Keys map to same slot → 127.0.0.1:7381 MGET foo {foo}.bar
Keys map to different slots and get split up into single slot ones routed to the according nodes
→ 127.0.0.1:7379 GET bar
→ 127.0.0.1:7381 GET foo
上面提供的一些简单示例,展示了Spring Data Redis的整体策略。意识到一些操作需要加载大量的数据到内存中,以便于计算出期望的结果。此外,并不是所有跨slot 的请求都可以安全的被分为多个单slot 请求,如果误用将会产生错误(如 PFCOUNT)。
3. Working With RedisTemplate and ClusterOperations
请参考Working with Objects through RedisTemplate 章节,获悉RedisTemplate 的一般用途、配置和使用情况。
请注意,当使用任何一个Json RedisSerializers来创建RedisTemplete#keySerializer时,改变json 结构会立即对哈希槽的计算产生影响。
RedisTemplate 通过ClusterOperations 接口可以对进行执行特定的操作,而该接口的获取需要调用RedisTemplate.opsForCluster()。可以明确的在集群的单个节点上执行一些命令,同时为模版保留了反序列化/序列化特性,并提供了管理命令像CLUSTER MEET,或更高级的操作如 resharding。
Example 4. Accessing RedisClusterConnection via RedisTemplate
ClusterOperations clusterOps = redisTemplate.opsForCluster();
clusterOps.shutdown(NODE_7379);
Shut down node at 7379 and cross fingers there is a slave in place that can take over.