Spring Data Redis(Redis Cluster)
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<String> nodes;
/**
* Get initial collection of known cluster nodes in format {@code host:port}.
*
* @return
*/
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> 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.