Redis 集群
Redis 集群
使用 Redis Cluster 需要 Redis Server 版本 3.0 或更高版本。更多信息请参阅 Cluster Tutorial。
在使用 Redis 仓库 与 Redis 集群时,请确保熟悉如何在集群上 运行 Redis 仓库。
在使用 Redis 集群时,不要依赖键空间事件,因为键空间事件不会在分片之间复制。Pub/Sub 会随机订阅一个集群节点,该节点只能接收来自单个分片的键空间事件。为了避免键空间事件丢失,请使用单节点 Redis。
使用 Redis 集群连接
Redis Cluster 的行为与单节点 Redis 甚至由 Sentinel 监控的主从复制环境不同。这是因为自动分片机制将一个键映射到 16384
个槽中的一个,这些槽分布在各个节点上。因此,涉及多个键的命令必须确保所有键都映射到完全相同的槽,以避免跨槽错误。单个集群节点仅服务于一组特定的键。向特定服务器发出的命令仅返回该服务器所服务的键的结果。举一个简单的例子,考虑 KEYS
命令。在集群环境中向服务器发出该命令时,它仅返回请求发送到的节点所服务的键,而不一定是集群中的所有键。因此,要在集群环境中获取所有键,必须从所有已知的主节点中读取键。
虽然特定键到对应槽服务节点的重定向由驱动库处理,但更高级别的功能,例如跨节点收集信息或向集群中的所有节点发送命令,则由 RedisClusterConnection
负责。以之前提到的键为例,这意味着 keys(pattern)
方法会获取集群中的每个主节点,并同时在每个主节点上运行 KEYS
命令,同时收集结果并返回累积的键集。如果仅请求单个节点的键,RedisClusterConnection
为这些方法提供了重载版本(例如 keys(node, pattern)
)。
RedisClusterNode
可以通过 RedisClusterConnection.clusterGetNodes
获取,也可以通过使用主机和端口或节点 ID 来构造。
以下示例展示了在集群中运行的一组命令:
示例 1. 在集群中运行命令的示例
redis-cli@127.0.0.1:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460 // <1>
7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-20252 // <2>
164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-20253 // <3>
b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected // <4>
RedisClusterConnection connection = connectionFactory.getClusterConnection();
connection.set("thing1", value); 5
connection.set("thing2", value); 6
connection.keys("*"); 7
connection.keys(NODE_7379, "*"); 8
connection.keys(NODE_7380, "*"); 9
connection.keys(NODE_7381, "*"); 10
connection.keys(NODE_7382, "*"); 11
主节点服务于槽 0 到 5460,复制到位于 7382 的副本节点
主节点服务于槽 5461 到 10922
主节点服务于槽 10923 到 16383
副本节点持有位于 7379 的主节点的副本
请求路由到位于 7381 的节点,服务于槽 12182
请求路由到位于 7379 的节点,服务于槽 5061
请求路由到位于 7379、7380、7381 的节点 → [thing1, thing2]
请求路由到位于 7379 的节点 → [thing2]
请求路由到位于 7380 的节点 → []
请求路由到位于 7381 的节点 → [thing1]
请求路由到位于 7382 的节点 → [thing2]
当所有键映射到同一个槽时,原生驱动库会自动处理跨槽请求,例如 MGET
。然而,一旦不是这种情况,RedisClusterConnection
会针对服务槽的节点运行多个并行的 GET
命令,并再次返回累积结果。这种方式比单槽方法的性能要差,因此应谨慎使用。如有疑问,可以考虑通过提供大括号中的前缀将键固定到同一个槽,例如 {my-prefix}.thing1
和 {my-prefix}.thing2
,它们都将映射到同一个槽编号。以下示例展示了跨槽请求的处理:
示例 2. 跨槽请求处理的样例
redis-cli@127.0.0.1:7379 > cluster nodes
6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460 // <1>
7bb...
RedisClusterConnection connection = connectionFactory.getClusterConnection();
connection.set("thing1", value); // slot: 12182
connection.set("{thing1}.thing2", value); // slot: 12182
connection.set("thing2", value); // slot: 5461
connection.mGet("thing1", "{thing1}.thing2"); 2
connection.mGet("thing1", "thing2"); 3
与之前示例中的配置相同。
键映射到同一个槽 → 127.0.0.1:7381 MGET thing1 {thing1}.thing2
键映射到不同的槽,并被拆分为单槽请求路由到相应的节点
→ 127.0.0.1:7379 GET thing2
→ 127.0.0.1:7381 GET thing1
前面的示例展示了 Spring Data Redis 所遵循的通用策略。请注意,某些操作可能需要将大量数据加载到内存中以计算所需的命令。此外,并非所有的跨槽请求都可以安全地转换为多个单槽请求,如果使用不当可能会导致错误(例如,PFCOUNT
)。
使用 RedisTemplate
和 ClusterOperations
有关 RedisTemplate
的通用用途、配置和使用信息,请参阅通过 RedisTemplate 处理对象部分。
:::注意
在使用任何 JSON RedisSerializer
设置 RedisTemplate#keySerializer
时,请务必小心,因为更改 JSON 结构会立即影响哈希槽的计算。
:::
RedisTemplate
通过 ClusterOperations
接口提供对集群特定操作的访问,该接口可以通过 RedisTemplate.opsForCluster()
获得。这使您能够在集群中的单个节点上显式运行命令,同时保留为模板配置的序列化和反序列化功能。它还提供管理命令(例如 CLUSTER MEET
)或更高级别的操作(例如重新分片)。
以下示例展示了如何使用 RedisTemplate
访问 RedisClusterConnection
:
示例 3. 使用 RedisTemplate
访问 RedisClusterConnection
ClusterOperations clusterOps = redisTemplate.opsForCluster();
clusterOps.shutdown(NODE_7379); 1
关闭位于 7379 的节点,并祈祷有一个副本可以接管。
Redis Cluster 管道目前仅支持通过 Lettuce 驱动使用,除了以下跨槽(cross-slot)键的命令外:rename
、renameNX
、sort
、bLPop
、bRPop
、rPopLPush
、bRPopLPush
、info
、sMove
、sInter
、sInterStore
、sUnion
、sUnionStore
、sDiff
、sDiffStore
。对于同槽(same-slot)键的支持是完全的。