跳到主要内容

Redis 集群

DeepSeek V3 中英对照 Redis Cluster

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>
text
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
java
  • 主节点服务于槽 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...
text
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
java
  • 与之前示例中的配置相同。

  • 键映射到同一个槽 → 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)。

使用 RedisTemplateClusterOperations

有关 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
java
  • 关闭位于 7379 的节点,并祈祷有一个副本可以接管。

备注

Redis Cluster 管道目前仅支持通过 Lettuce 驱动使用,除了以下跨槽(cross-slot)键的命令外:renamerenameNXsortbLPopbRPoprPopLPushbRPopLPushinfosMovesIntersInterStoresUnionsUnionStoresDiffsDiffStore。对于同槽(same-slot)键的支持是完全的。