6. Redis 集群 (Cluster) (Redis 3.0+)


文档摘要

Redis 集群 (Cluster) (Redis 3.0+) Redis 集群 (Cluster) 详解与实践 (Redis 3.0+) Redis 集群(Cluster)是 Redis 3.0 版本及以后引入的分布式解决方案,旨在提供 高可用、高性能 和 可扩展性 的 Redis 服务。与单机 Redis 或主从复制模式相比,集群能够自动将数据分散存储在多个 Redis 节点上,并提供故障转移能力,从而应对更大规模的数据和更高的并发访问。 本文将深入探讨 Redis 集群的核心概念、架构原理、搭建部署、客户端连接、数据分片、故障转移以及实际的代码操作,帮助您全面理解和应用 Redis 集群技术。

6. Redis 集群 (Cluster) (Redis 3.0+)

Redis 集群 (Cluster) 详解与实践 (Redis 3.0+)

Redis 集群(Cluster)是 Redis 3.0 版本及以后引入的分布式解决方案,旨在提供 高可用高性能可扩展性 的 Redis 服务。与单机 Redis 或主从复制模式相比,集群能够自动将数据分散存储在多个 Redis 节点上,并提供故障转移能力,从而应对更大规模的数据和更高的并发访问。

本文将深入探讨 Redis 集群的核心概念、架构原理、搭建部署、客户端连接、数据分片、故障转移以及实际的代码操作,帮助您全面理解和应用 Redis 集群技术。

1. Redis 集群的核心优势与应用场景

在深入细节之前,我们先来了解 Redis 集群的核心优势和适用的场景:

核心优势:

  • 高可用性 (High Availability, HA): 集群通过主从复制和自动故障转移机制,能够在部分节点失效的情况下仍然保持服务的可用性。当主节点宕机时,其从节点会自动晋升为新的主节点,无需人工干预。

  • 可扩展性 (Scalability): 集群允许通过增加节点来线性扩展存储容量和处理能力。数据被自动分片到不同的节点上,使得集群能够处理海量数据和高并发请求。

  • 高性能 (Performance): 虽然引入了分布式架构,但 Redis 集群仍然保持了 Redis 本身的高性能特性。通过合理的数据分片和节点部署,可以提升整体的读写性能。

  • 自动故障转移 (Automatic Failover): 集群能够自动检测节点故障,并在主节点失效时,选举从节点接替工作,实现服务的自动恢复。

适用场景:

  • 大规模缓存: 当单机 Redis 无法满足数据存储需求时,集群是理想的选择,可以将缓存分散到多个节点,支持 PB 级别的数据量。

  • 高并发读写: 对于需要处理高并发读写请求的应用,集群可以将请求分散到不同的节点,提高整体吞吐量。

  • 关键业务数据存储: 集群的高可用性特性使其适用于存储关键业务数据,确保在硬件故障等情况下数据和服务仍然可用。

  • 分布式锁: 虽然 Redis 集群在分布式锁方面有一些限制 (跨槽操作),但仍然可以通过 Redlock 算法等方式实现高可用分布式锁。

2. Redis 集群架构与核心概念

Redis 集群采用 无中心化 的分布式架构,这意味着集群中没有中心节点来管理整个集群的状态和数据分配。每个节点都是平等的,通过 Gossip 协议 互相通信,共享集群信息。

关键组件和概念:

  • Redis 节点 (Node): 集群由多个 Redis 节点组成,每个节点都是一个独立的 Redis 实例。节点可以是主节点 (Master) 或从节点 (Replica)。

  • 主节点 (Master): 负责处理客户端的读写请求,存储实际的数据。每个主节点负责一部分数据槽 (Hash Slot)。

  • 从节点 (Replica): 复制主节点的数据,用于提供数据冗余和读扩展。当主节点失效时,从节点可以被选举为新的主节点。

  • 哈希槽 (Hash Slot): Redis 集群将整个键空间划分为 16384 个哈希槽 (slot)。每个键根据 CRC16 算法计算后对 16384 取模,被映射到一个哈希槽。每个哈希槽由一个主节点负责。

  • 集群总线 (Cluster Bus): 节点之间通过集群总线(Cluster Bus)进行通信,使用特殊的端口 (默认端口 + 10000) 和二进制协议。集群总线用于节点间的 Gossip 消息传播、故障检测、配置更新和故障转移等。

  • Gossip 协议: 节点之间通过 Gossip 协议交换集群信息,例如节点状态、槽分配信息等。Gossip 协议具有最终一致性,保证集群状态最终同步。

  • 客户端重定向 (Redirection): 客户端连接到集群中的任意节点,节点会根据键的哈希槽,将客户端重定向到负责该槽的节点。客户端需要能够处理重定向响应。

集群架构图 (示意):

+-------+ +-------+ +-------+ | Master|---->|Replica| | Master|---->|Replica| | Master|---->|Replica|| [7001]| | [7002]| | [7003]| | [7004]| | [7005]| | [7006]| +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ | | | | | | +-------------+-------------+-------------+-------------+-------------+ Cluster Bus (Gossip Protocol)

3. 搭建 Redis 集群环境 (代码实践)

接下来,我们通过代码实践来搭建一个简单的 Redis 集群。这里我们使用 Docker 来快速搭建环境,并使用 redis-cli 工具进行集群管理。

3.1 准备 Docker 环境 (可选)

如果您已经有 Redis 环境,可以跳过此步骤。如果您使用 Docker,请确保已安装 Docker 和 Docker Compose。

创建一个 docker-compose.yml 文件,内容如下:

version: '3.8' services: redis1: image: redis:7.0 ports: - "7001:6379" - "17001:17001" # Cluster bus port command: redis-server --cluster-enabled yes --cluster-config-file nodes-7001.conf --cluster-node-timeout 15000 --appendonly yes volumes: - ./redis_data/redis1:/data networks: - redis-cluster redis2: image: redis:7.0 ports: - "7002:6379" - "17002:17002" command: redis-server --cluster-enabled yes --cluster-config-file nodes-7002.conf --cluster-node-timeout 15000 --appendonly yes volumes: - ./redis_data/redis2:/data networks: - redis-cluster redis3: image: redis:7.0 ports: - "7003:6379" - "17003:17003" command: redis-server --cluster-enabled yes --cluster-config-file nodes-7003.conf --cluster-node-timeout 15000 --appendonly yes volumes: - ./redis_data/redis3:/data networks: - redis-cluster redis4: image: redis:7.0 ports: - "7004:6379" - "17004:17004" command: redis-server --cluster-enabled yes --cluster-config-file nodes-7004.conf --cluster-node-timeout 15000 --appendonly yes volumes: - ./redis_data/redis4:/data networks: - redis-cluster redis5: image: redis:7.0 ports: - "7005:6379" - "17005:17005" command: redis-server --cluster-enabled yes --cluster-config-file nodes-7005.conf --cluster-node-timeout 15000 --appendonly yes volumes: - ./redis_data/redis5:/data networks: - redis-cluster redis6: image: redis:7.0 ports: - "7006:6379" - "17006:17006" command: redis-server --cluster-enabled yes --cluster-config-file nodes-7006.conf --cluster-node-timeout 15000 --appendonly yes volumes: - ./redis_data/redis6:/data networks: - redis-cluster networks: redis-cluster:

这个 docker-compose.yml 文件定义了 6 个 Redis 节点,端口分别为 7001-7006。每个节点都启用了集群模式 (--cluster-enabled yes),并指定了配置文件、节点超时时间和 AOF 持久化。

docker-compose.yml 文件所在的目录下,执行以下命令启动 Docker 容器:

docker-compose up -d

3.2 创建 Redis 集群

启动容器后,我们可以使用 redis-cli 工具来创建集群。连接到任意一个 Redis 节点 (例如 redis1):

docker exec -it redis1 redis-cli

redis-cli 中,使用 cluster create 命令创建集群。我们需要指定集群中所有 主节点 的 IP 地址和端口。 为了简化,我们这里假设所有节点都在同一台机器上,使用容器内部网络。

127.0.0.1:6379> cluster create 172.18.0.2:7001 172.18.0.3:7003 172.18.0.4:7005 172.18.0.5:7002 172.18.0.6:7004 172.18.0.7:7006 --cluster-replicas 1
  • cluster create: 创建集群命令。

  • 172.18.0.2:7001 ... 172.18.0.7:7006: 指定集群中的所有节点地址和端口。这里需要替换成您实际的容器 IP 地址和端口。您可以使用 docker inspect redis1 等命令查看容器的 IP 地址。 请注意,这里我们指定的是所有节点的地址,而不仅仅是主节点。redis-cli 会自动判断并分配主从关系。

  • --cluster-replicas 1: 指定每个主节点配备 1 个从节点。

执行命令后,redis-cli 会输出集群配置信息,并提示是否接受配置。输入 yes 确认创建集群。

>>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica to Master[0] -> 172.18.0.5:7002 Adding replica to Master[1] -> 172.18.0.6:7004 Adding replica to Master[2] -> 172.18.0.7:7006 >>> Trying to optimize allocation for replication and failovers... >>> Performing replica resharding... [OK] All replicas resharded. [OK] New cluster created with 3 masters and 3 replicas. [OK] Assigning slots on masters... [OK] All 16384 slots covered. >>> Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assigning keys to slots... >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join .... >>> Performing Cluster Check (using node 172.18.0.2:7001) M: 7064a81c76c9042f297448129333893f283c401e 172.18.0.2:7001 slots:[0-5460] (5461 slots) master 1 additional replica(s) R: e64867639347f486a997c8c138568481c2f24678 172.18.0.5:7002 slots: (0 slots) replica replicates 7064a81c76c9042f297448129333893f283c401e M: 2c07b66683b0678423c33784251764873736d212 172.18.0.3:7003 slots:[5461-10922] (5462 slots) master 1 additional replica(s) R: 49d7766046f939577c23b539583556705d722852 172.18.0.6:7004 slots: (0 slots) replica replicates 2c07b66683b0678423c33784251764873736d212 M: 6d2191660602115950990319722c489372359513 172.18.0.4:7005 slots:[10923-16383] (5461 slots) master 1 additional replica(s) R: 480234680d752b1d9489a82c2687c57269b10d23 172.18.0.7:7006 slots: (0 slots) replica replicates 6d2191660602115950990319722c489372359513 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.

看到 [OK] All 16384 slots covered.[OK] New cluster created with ... 等信息,表示集群创建成功。

3.3 检查集群状态

使用 cluster info 命令查看集群状态:

127.0.0.1:6379> cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:6 cluster_current_epoch:6 cluster_my_epoch:6 cluster_stats_messages_ping_sent:56 cluster_stats_messages_pong_sent:58 cluster_stats_messages_sent:114 cluster_stats_messages_ping_received:58 cluster_stats_messages_pong_received:56 cluster_stats_messages_meet_received:5 cluster_stats_messages_received:119 cluster_stats_messages_current_queue_length:0 cluster_stats_messages_max_queue_length:1

cluster_state:ok 表示集群状态正常。cluster_slots_ok:16384 表示所有 16384 个槽都已分配。 cluster_size:6 表示集群中有 6 个节点。

使用 cluster nodes 命令查看集群节点信息:

127.0.0.1:6379> cluster nodes 7064a81c76c9042f297448129333893f283c401e 172.18.0.2:7001@17001 master, myself - 0 1688726829000 1 connected 0-5460 e64867639347f486a997c8c138568481c2f24678 172.18.0.5:7002@17002 replica 7064a81c76c9042f297448129333893f283c401e 0 1688726829000 1 connected 2c07b66683b0678423c33784251764873736d212 172.18.0.3:7003@17003 master - 0 1688726829000 2 connected 5461-10922 49d7766046f939577c23b539583556705d722852 172.18.0.6:7004@17004 replica 2c07b66683b0678423c33784251764873736d212 0 1688726828000 2 connected 6d2191660602115950990319722c489372359513 172.18.0.4:7005@17005 master - 0 1688726828000 3 connected 10923-16383 480234680d752b1d9489a82c2687c57269b10d23 172.18.0.7:7006@17006 replica 6d2191660602115950990319722c489372359513 0 1688726828000 3 connected

可以看到每个节点的 ID、地址、角色 (master/replica)、负责的槽范围等信息。

4. 客户端连接 Redis 集群 (代码实践)

客户端连接 Redis 集群与连接单机 Redis 有些不同。客户端需要能够识别集群模式,并处理节点重定向。

4.1 使用 redis-cli 连接集群

使用 redis-cli 连接集群时,需要添加 -c 参数启用集群模式。

redis-cli -c -p 7001

连接成功后,您会看到终端提示符变为 127.0.0.1:7001>。 在集群模式下,redis-cli 会自动处理重定向。

4.2 使用 Python redis-py-cluster 客户端 (代码实践)

在 Python 中,可以使用 redis-py-cluster 客户端库来连接 Redis 集群。

首先安装 redis-py-cluster 库:

pip install redis-py-cluster

Python 代码示例:

from rediscluster import RedisCluster # 集群启动节点,客户端会从这些节点发现整个集群 startup_nodes = [{"host": "127.0.0.1", "port": "7001"}] # 创建 RedisCluster 客户端实例 rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True) # 设置键值对 rc.set("cluster_key", "hello cluster") # 获取键值对 value = rc.get("cluster_key") print(f"Get value: {value}") # 尝试操作不同槽位的 key rc.set("key1", "value1") # 假设 key1 槽位在 node1 rc.set("key2", "value2") # 假设 key2 槽位在 node2 print(f"Get key1: {rc.get('key1')}") print(f"Get key2: {rc.get('key2')}") # 批量操作 (pipeline) pipe = rc.pipeline() pipe.set("batch_key1", "batch_value1") pipe.set("batch_key2", "batch_value2") pipe.get("batch_key1") pipe.get("batch_key2") results = pipe.execute() print(f"Batch results: {results}") # 关闭连接 (可选,客户端通常会自动管理连接) # rc.close()

这个示例代码演示了如何使用 redis-py-cluster 客户端连接集群,进行 SET、GET 操作,以及批量操作。客户端库会自动处理节点发现和重定向,对用户透明。

4.3 其他语言客户端

Redis 官方和社区提供了多种语言的集群客户端库,例如 Java 的 Jedis Cluster, Lettuce Cluster, Go 的 go-redis/redis 等。 使用方法类似,都需要指定集群的启动节点,客户端库会自动处理集群拓扑和重定向。

5. Redis 集群数据分片与槽位 (Slot)

Redis 集群使用哈希槽 (Hash Slot) 来实现数据分片。 键空间被划分为 16384 个槽位,每个槽位由一个主节点负责。

5.1 槽位分配

在集群创建时,redis-cli 会自动将 16384 个槽位分配给集群中的主节点。您可以使用 cluster nodes 命令查看每个主节点负责的槽位范围。

例如,在上面的例子中,节点 172.18.0.2:7001 负责槽位 0-5460,节点 172.18.0.3:7003 负责槽位 5461-10922,节点 172.18.0.4:7005 负责槽位 10923-16383

5.2 键到槽位的映射

当客户端操作一个键时,Redis 会使用以下算法计算键对应的槽位:

  1. 计算键的 CRC16 值。

  2. 将 CRC16 值对 16384 取模,得到槽位 ID。

slot_id = CRC16(key) % 16384

根据槽位 ID,Redis 将请求路由到负责该槽位的主节点。

5.3 槽位重分配 (Resharding)

当集群需要扩容或缩容时,需要进行槽位重分配 (Resharding)。 Resharding 是将一部分槽位从一个节点迁移到另一个节点的过程。

可以使用 redis-clicluster reshard 命令手动触发槽位重分配。 Resharding 过程是自动化的,Redis 会逐步将槽位和数据从源节点迁移到目标节点,期间集群仍然可以对外提供服务。

代码示例:手动触发 Resharding (仅演示概念,生产环境谨慎操作)

# 连接到集群任意节点 redis-cli -c -p 7001 # 进入 cluster 模式 127.0.0.1:7001> cluster # 触发 resharding 命令 (需要指定目标节点、源节点等参数,比较复杂,这里仅演示命令) 127.0.0.1:7001 cluster reshard ...

实际的 cluster reshard 命令参数比较复杂,需要仔细阅读 Redis 文档。 通常情况下,集群的扩容和缩容可以通过自动化工具或运维平台来完成,无需手动执行 cluster reshard 命令。

5.4 跨槽操作的限制

Redis 集群对跨槽操作 (操作的键分布在不同的槽位) 有限制。 默认情况下,Redis 集群不支持跨槽的 MGET, MSET 等命令。 如果要操作多个键,且这些键可能分布在不同的槽位,需要使用 Hash Tag客户端分片 等技术来解决。

  • Hash Tag: 在键名中使用 {...} 包裹一部分字符串,Redis 会只对 {} 内的字符串进行哈希计算槽位。 例如,键名 user:{1000}.profileuser:{1000}.settings 会被哈希到同一个槽位,因为它们有相同的 Hash Tag {1000}

  • 客户端分片: 客户端根据键的哈希槽,将请求发送到对应的节点。 对于需要跨多个槽位的操作,客户端需要自行拆分请求,分别发送到不同的节点,并在客户端聚合结果。

6. Redis 集群的故障转移 (Failover)

Redis 集群具备自动故障转移能力,当主节点失效时,其从节点会自动晋升为新的主节点,保证服务的可用性。

6.1 故障检测

集群中的节点通过 Gossip 协议互相发送 Ping 消息进行心跳检测。 当一个节点在一段时间内 (由 cluster-node-timeout 参数配置) 没有收到某个节点的有效回复,则认为该节点可能下线 (PFail, Possible Fail)。

当集群中超过半数的主节点都将某个主节点标记为 PFail 时,该主节点会被标记为 Fail (Fail)。

6.2 故障转移过程

当一个主节点被标记为 Fail 时,集群会启动故障转移过程:

  1. 选举新的主节点: 失效主节点的从节点会参与选举,争夺成为新的主节点。 选举算法通常是基于 Raft 算法的变种。

  2. 从节点晋升: 选举获胜的从节点晋升为新的主节点,接替原主节点的工作。

  3. 槽位重新分配: 新的主节点接管原主节点负责的槽位。

  4. 集群状态更新: 集群通过 Gossip 协议将新的集群状态广播给所有节点。

6.3 手动触发故障转移 (测试目的)

为了测试故障转移,您可以手动模拟主节点故障。 例如,使用 redis-cli debug kill 命令强制关闭一个主节点 (仅用于测试环境,生产环境禁用!)。

代码示例:模拟主节点故障 (仅测试环境)

# 连接到主节点 (例如 7001) redis-cli -p 7001 # 强制关闭节点 (危险操作,仅测试) 127.0.0.1:7001> DEBUG KILL OK

关闭主节点后,观察集群状态,等待一段时间 (通常几秒到几十秒),从节点会自动晋升为新的主节点。

可以使用 cluster nodes 命令查看节点角色变化。 原来的主节点会变为 fail? 状态,而其从节点会变为 master 状态。

6.4 持久化与故障转移

为了保证数据在故障转移后不丢失,建议开启 Redis 的持久化机制 (RDB 或 AOF)。 当从节点晋升为主节点后,会加载持久化文件,恢复数据。

7. Redis 集群的扩容与缩容

Redis 集群支持在线扩容和缩容,可以在不停止服务的情况下增加或减少节点。

7.1 集群扩容 (Adding Nodes)

扩容集群通常需要添加新的主节点和从节点。

  1. 启动新节点: 启动新的 Redis 实例,并配置为集群模式。

  2. 加入集群: 使用 redis-cli cluster meet 命令将新节点加入到现有集群中。

  3. 槽位重分配 (Resharding): 使用 redis-cli cluster reshard 命令将一部分槽位从现有节点迁移到新节点。

7.2 集群缩容 (Removing Nodes)

缩容集群需要移除不再需要的节点。

  1. 迁移槽位: 如果需要移除的节点是主节点,需要先将其负责的槽位迁移到其他主节点。 使用 redis-cli cluster reshard 命令进行槽位迁移。

  2. 移除节点: 使用 redis-cli cluster forget 命令将要移除的节点从集群中移除。

  3. 关闭节点: 关闭被移除的 Redis 实例。

集群扩容和缩容操作相对复杂,需要仔细规划和操作。 建议使用自动化工具或运维平台来管理集群的扩容和缩容。

8. Redis 集群的监控与运维

对于生产环境的 Redis 集群,监控和运维至关重要。 需要监控集群的健康状态、性能指标、故障告警等。

常用的监控指标:

  • 集群状态 (cluster_state): 是否为 ok

发布者: 作者: 转发
评论区 (0)
U