6.1 Redis Cluster 架构 6.1 Redis Cluster 架构详解:代码实践与深度解析 6.1.1 Redis Cluster 架构概览 Redis Cluster 采用去中心化的架构设计,集群中的节点彼此对等,不存在中心化的配置管理节点或代理节点。每个节点都存储一部分数据,并与其他节点保持通信,共同维护集群的稳定运行。 核心特点: 数据分片 (Data Sharding): Redis Cluster 将所有数据划分为 16384 个槽 (Slot),每个槽可以存储任意数量的键值对。集群中的每个节点负责管理一部分槽,以及槽对应的数据。 去中心化 (Decentralized): 集群中没有中心节点,所有节点都是平等的,通过 Gossip 协议进行通信和信息同步。
Redis Cluster 采用去中心化的架构设计,集群中的节点彼此对等,不存在中心化的配置管理节点或代理节点。每个节点都存储一部分数据,并与其他节点保持通信,共同维护集群的稳定运行。
核心特点:
数据分片 (Data Sharding): Redis Cluster 将所有数据划分为 16384 个槽 (Slot),每个槽可以存储任意数量的键值对。集群中的每个节点负责管理一部分槽,以及槽对应的数据。
去中心化 (Decentralized): 集群中没有中心节点,所有节点都是平等的,通过 Gossip 协议进行通信和信息同步。
高可用 (High Availability): 每个 Master 节点可以拥有多个 Slave 节点,当 Master 节点发生故障时,Slave 节点可以自动晋升为 Master,保证数据和服务的高可用性。
自动故障转移 (Automatic Failover): 集群能够自动检测节点故障,并进行故障转移,无需人工干预。
线性扩展 (Linear Scalability): 可以通过简单地添加节点来扩展集群的容量和性能,实现线性扩展。
架构图示 (简化的逻辑视图):
+----------------+ +----------------+ +----------------+ | Redis Node 1 |---| Redis Node 2 |---| Redis Node 3 | ... | (Master/Slave) |---| (Master/Slave) |---| (Master/Slave) || Slots: 0-5460 |---| Slots: 5461-10922|---| Slots: 10923-16383| +----------------+ +----------------+ +----------------+ | | | +-----------------------+-----------------------+ Gossip Network
理解 Redis Cluster 的架构,需要掌握以下核心组件和概念:
Redis Cluster 由多个 Redis 节点组成,每个节点运行着 Redis 服务器实例。节点可以是 Master 节点 或 Slave 节点。
Master 节点: 负责存储数据、处理客户端请求,是数据读写的入口。每个 Master 节点负责管理一部分槽以及槽对应的数据。
Slave 节点: 是 Master 节点的副本,用于数据备份和提高读取性能。Slave 节点从 Master 节点异步复制数据,当 Master 节点故障时,Slave 节点可以被选举为新的 Master 节点。
一个典型的 Redis Cluster 部署通常包含多个 Master 节点,为了保证高可用,每个 Master 节点通常会配备至少一个 Slave 节点。
Redis Cluster 将所有键值对数据划分为 16384 个槽,编号从 0 到 16383。槽是 Redis Cluster 数据分片和管理的最小单元。
槽分配: 每个 Master 节点负责管理一部分槽,槽的分配信息存储在集群的元数据中,并同步到所有节点。
键到槽的映射: 当客户端操作一个键时,Redis Cluster 会根据键使用 CRC16 算法计算哈希值,然后将哈希值对 16384 取模,得到键对应的槽号。公式如下:
slot_number = CRC16(key) % 16384
数据定位: 通过槽号,Redis Cluster 可以快速定位到键对应的数据存储在哪个 Master 节点上。
代码实践:查看键对应的槽号
可以使用 redis-cli 的 CLUSTER KEYSLOT 命令查看指定键对应的槽号。
redis-cli -c -h <node_ip> -p <node_port> > CLUSTER KEYSLOT mykey (integer) 12705
上述命令会连接到指定节点,并返回键 "mykey" 对应的槽号为 12705。
Redis Cluster 节点之间通过 集群总线 (Cluster Bus) 进行通信。集群总线是一个独立的 TCP 连接通道,用于节点之间的 Gossip 消息传播、故障检测、配置更新、数据迁移等。
Gossip 协议: 节点通过 Gossip 协议交换集群信息,包括节点状态、槽分配信息等。Gossip 协议具有最终一致性,保证集群信息的最终同步。
节点发现: 新节点加入集群时,通过 Gossip 协议与其他节点建立连接,并同步集群信息。
故障检测: 节点之间定期发送 PING 消息,检测其他节点是否存活。如果一个节点在一段时间内没有收到某个节点的 PING 消息,则认为该节点可能故障。
配置传播: 集群配置的更新(例如槽分配变更、节点角色变更)通过 Gossip 协议传播到整个集群。
代码实践:查看集群节点信息
可以使用 redis-cli 的 CLUSTER NODES 命令查看集群中所有节点的信息。
redis-cli -c -h <node_ip> -p <node_port> > CLUSTER NODES ... (输出集群节点信息,包括节点 ID, IP:Port, 角色, 槽分配等) ...
输出结果会显示集群中所有节点的详细信息,例如:
7e7a9a0b1b4d8f8a5c6c7d8e9f0a1b2c3d4e5f67 127.0.0.1:7001@17001 master - 0 1699999999000 1 connected 0-5460 8f8b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6789 127.0.0.1:7002@17002 slave 7e7a9a0b1b4d8f8a5c6c7d8e9f0a1b2c3d4e5f67 0 1699999999000 2 connected ...
每一行代表一个节点的信息,字段含义包括:
节点 ID: 节点的唯一标识符。
IP:Port@GossipPort: 节点的 IP 地址、客户端端口和 Gossip 端口。Gossip 端口通常是客户端端口加 10000。
角色: 节点的角色,master 或 slave。
Master 节点 ID (如果是 Slave): 所属 Master 节点的 ID。
最后一次 PING 成功时间戳: 上次成功收到 PING 消息的时间戳。
连接状态: connected 表示连接正常。
槽分配范围 (如果是 Master): 该 Master 节点负责管理的槽范围。
当客户端请求的键对应的槽不在客户端连接的节点上时,Redis Cluster 会返回 MOVED 重定向 或 ASK 重定向 错误。
MOVED 重定向: 表示键对应的槽已经永久迁移到新的节点。客户端需要更新本地路由表,并将后续请求发送到新的节点。
ASK 重定向: 表示键对应的槽正在迁移过程中。客户端需要先向目标节点发送 ASKING 命令,然后再发送实际的请求命令。
客户端需要实现智能的重定向处理逻辑,才能正确地与 Redis Cluster 交互。 官方推荐使用 Redis Cluster 客户端库,这些库通常已经封装了重定向处理的细节。
代码实践:手动处理 MOVED 重定向 (Python 示例)
以下 Python 代码示例演示了如何手动处理 MOVED 重定向错误 (仅为演示原理,实际应用建议使用 Redis Cluster 客户端库):
import redis def execute_command(client, command, *args): try: return client.execute_command(command, *args) except redis.exceptions.ResponseError as e: if "MOVED" in str(e): slot, new_host_port = str(e).split(" ")[1:] new_host, new_port = new_host_port.split(":") print(f"MOVED redirection: Slot {slot} moved to {new_host}:{new_port}") # 更新客户端路由表 (此处简化,实际应用需要更完善的路由表管理) new_client = redis.Redis(host=new_host, port=int(new_port)) return execute_command(new_client, command, *args) # 递归调用 else: raise e # 连接到任意一个 Redis Cluster 节点 client = redis.Redis(host='127.0.0.1', port=7001) # 尝试设置一个键值对 key = "mykey" value = "myvalue" result = execute_command(client, "SET", key, value) print(f"SET command result: {result}") # 尝试获取该键的值 result = execute_command(client, "GET", key) print(f"GET command result: {result}")
这段代码简单地捕获 redis.exceptions.ResponseError 异常,判断是否包含 "MOVED" 字符串。如果是 MOVED 重定向,则解析新的节点地址,并重新连接到新的节点执行命令。实际的 Redis Cluster 客户端库会更智能地维护路由表,并处理各种重定向情况。
Redis Cluster 使用 哈希槽 (Hash Slot) 来实现数据分片。了解数据分片的原理对于理解集群的数据分布和性能至关重要。
在集群初始化或节点加入/离开时,需要进行哈希槽的分配。每个 Master 节点会被分配一定范围的槽。槽的分配策略通常是尽量均衡,使得每个 Master 节点负责的槽数量相近。
代码实践:查看槽分配信息
可以使用 redis-cli 的 CLUSTER INFO 命令查看集群的槽分配状态。
redis-cli -c -h <node_ip> -p <node_port> > 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:3 cluster_current_epoch:3 cluster_my_epoch:3 cluster_stats_messages_ping_sent:12345 cluster_stats_messages_pong_sent:67890 cluster_stats_messages_sent:80235 cluster_stats_messages_ping_received:9876 cluster_stats_messages_pong_received:54321 cluster_stats_messages_meet_received:123 cluster_stats_messages_received:64632
关注 cluster_slots_assigned 和 cluster_slots_ok 字段,它们分别表示已分配的槽数量和状态正常的槽数量。正常情况下,这两个值都应该等于 16384。
当客户端写入数据时,Redis Cluster 会根据键计算槽号,然后将数据存储到负责该槽号的 Master 节点上。
均匀分布: 理想情况下,通过合理的槽分配和哈希算法,数据可以均匀地分布在集群的各个 Master 节点上,从而实现负载均衡。
数据迁移: 当集群扩容或缩容时,需要进行槽的重新分配和数据迁移。Redis Cluster 支持在线数据迁移,保证服务不中断。
数据迁移过程:
重新分配槽: 管理员调整槽的分配方案,例如将一部分槽从一个节点迁移到另一个节点。
数据迁移: 源节点将负责的槽中的数据逐步迁移到目标节点。迁移过程中,客户端仍然可以访问数据,但可能会遇到 ASK 重定向。
槽分配更新: 迁移完成后,集群更新槽的分配信息,并将新的槽分配方案同步到所有节点。
Redis Cluster 的高可用性主要通过 主从复制 (Master-Slave Replication) 和 自动故障转移 (Automatic Failover) 机制来保障。
每个 Master 节点可以配置一个或多个 Slave 节点。Slave 节点异步地从 Master 节点复制数据。
数据备份: Slave 节点作为 Master 节点的数据备份,防止数据丢失。
读写分离 (可选): 可以配置 Slave 节点处理读请求,分担 Master 节点的读压力 (需要注意数据一致性问题)。
故障转移基础: 当 Master 节点故障时,Slave 节点可以接替 Master 节点的工作。
当集群中的 Master 节点发生故障时,集群会自动进行故障转移,将一个 Slave 节点提升为新的 Master 节点,保证服务的持续可用性。
故障转移过程:
故障检测: 集群中的其他节点通过 Gossip 协议检测到 Master 节点故障 (例如,PING 超时)。
Slave 选举: 故障 Master 节点的 Slave 节点开始进行选举,选出一个新的 Master 节点。选举过程基于 Raft 算法的变种。
Slave 晋升: 选举出的 Slave 节点晋升为新的 Master 节点,接管原 Master 节点负责的槽和数据。
集群通知: 集群将新的 Master 节点信息同步到所有节点,客户端路由表也会更新。
代码实践:模拟 Master 节点故障 (手动故障转移)
在实际生产环境中,故障转移是自动进行的。这里演示手动模拟 Master 节点故障,并观察 Slave 节点晋升的过程 (仅为演示原理,请勿在生产环境操作)。
假设我们有一个包含 3 个 Master 节点和 3 个 Slave 节点的集群。
查看节点信息: 使用 CLUSTER NODES 命令查看节点信息,找到一个 Master 节点及其 Slave 节点。
手动关闭 Master 节点: 使用 redis-cli shutdown 命令手动关闭选定的 Master 节点。
观察 Slave 节点晋升: 再次使用 CLUSTER NODES 命令查看节点信息。稍等片刻,你会发现原 Master 节点的 Slave 节点的角色已经变为 master,并且接管了原 Master 节点负责的槽。
注意: 手动关闭 Master 节点属于破坏性操作,请谨慎操作,并在测试环境中进行。
为了更好地理解 Redis Cluster 架构,我们来实践搭建一个本地 Redis Cluster 集群。
环境准备:
Redis 版本: Redis 3.0 或以上版本。
操作系统: Linux 或 macOS (Windows 环境配置稍复杂)。
端口: 需要开放多个端口 (例如 7000-7005 用于节点客户端端口,8000-8005 用于 Gossip 端口)。
步骤:
创建节点配置文件: 为每个节点创建一个配置文件 redis-node-XXXX.conf (例如 redis-node-7001.conf, redis-node-7002.conf ... redis-node-7006.conf)。
配置文件示例 (redis-node-7001.conf):
port 7001 cluster-enabled yes cluster-config-file nodes-7001.conf cluster-node-timeout 15000 appendonly yes
关键配置项:
port: 节点客户端端口。
cluster-enabled yes: 启用集群模式。
cluster-config-file nodes-XXXX.conf: 集群配置文件名,用于持久化集群配置信息。
cluster-node-timeout 15000: 节点超时时间 (毫秒)。
appendonly yes: 开启 AOF 持久化 (可选,建议开启)。
为每个节点修改 port 和 cluster-config-file 中的端口号。
启动节点: 分别启动 6 个 Redis 节点 (3 Master + 3 Slave)。
redis-server redis-node-7001.conf redis-server redis-node-7002.conf redis-server redis-node-7003.conf redis-server redis-node-7004.conf redis-server redis-node-7005.conf redis-server redis-node-7006.conf
创建集群: 使用 redis-cli --cluster create 命令创建集群。
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
命令参数:
redis-cli --cluster create: 创建集群命令。
127.0.0.1:7001 ... 127.0.0.1:7006: 集群节点的地址和端口。
--cluster-replicas 1: 每个 Master 节点配置 1 个 Slave 节点。
执行命令后,redis-cli 会提示你确认集群配置,输入 yes 确认创建。
连接集群: 使用 redis-cli -c 命令连接到集群。
redis-cli -c -h 127.0.0.1 -p 7001
-c 参数表示以集群模式连接。连接到任意一个节点即可,客户端会自动处理重定向。
测试集群: 尝试设置和获取键值对,观察集群行为。
127.0.0.1:7001> set mykey myvalue -> Redirected to slot [12705] located at 127.0.0.1:7003 OK 127.0.0.1:7003> get mykey "myvalue"
可以看到,客户端连接到 7001 端口的节点,但键 "mykey" 的槽 12705 位于 7003 端口的节点上,客户端被自动重定向到 7003 端口的节点进行操作。
至此,你已经成功搭建了一个本地 Redis Cluster 集群。
Redis Cluster 架构为 Redis 带来了水平扩展和高可用能力,使其能够应对大规模数据和高并发访问场景。理解 Redis Cluster 的架构设计、核心组件和运行机制,对于构建高性能、高可靠的分布式 Redis 应用至关重要。
总结 Redis Cluster 架构的核心要点:
去中心化架构,节点对等。
哈希槽分片,数据均匀分布。
Gossip 协议,集群信息同步和故障检测。
主从复制和自动故障转移,保障高可用。
客户端智能重定向,透明访问集群数据。
未来展望:
随着 Redis 的不断发展,Redis Cluster 也在持续演进。未来,Redis Cluster 可能会在以下方面继续增强:
更灵活的扩容和缩容机制。
更强大的监控和管理工具。
**与其他技术的更深度集成 (例如容器化、云原生)。