尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Redis学习笔记:Cluster 分片集群从原理到实战部署

Redis学习笔记:Cluster 分片集群从原理到实战部署 本文首发于 栏轩·阁欢迎访问阅读原文获取更好的阅读体验。一、什么是 Redis ClusterRedis Cluster 是 Redis官方提供的分布式解决方案从 Redis 3.0 开始引入。它通过数据分片Sharding和自动故障转移Failover实现了水平扩展将数据自动分布到多个节点高可用性部分节点故障时集群仍可正常工作去中心化架构无中心代理节点所有节点通过 Gossip 协议通信解决了什么问题问题说明单机容量瓶颈单个 Redis 实例最多存储几个 GB 到几十 GB 数据Cluster 可以将数据分散到多台机器单点故障主节点宕机后从节点自动晋升无需人工干预读写压力多主节点并行处理请求写入吞吐量线性提升扩容困难传统方式需要停机迁移数据Cluster 支持在线动态扩缩容二、Redis 部署方案对比特性单机实例主从复制Sentinel 哨兵Cluster 分片集群数据容量受单机内存限制受单机内存限制受单机内存限制可扩展至多台机器写入性能单点写入单点写入主单点写入主多主写入线性扩展自动故障转移❌❌✅✅自动分片❌❌❌✅在线扩缩容❌❌❌✅配置复杂度⭐ 简单⭐⭐⭐⭐⭐⭐⭐⭐⭐适用场景开发/缓存读写分离高可用场景大规模高可用场景什么时候选择 Cluster数据量超过单机内存建议单机 ≤ 16GB需要高写入吞吐量要求自动故障转移和高可用性希望在线扩缩容避免停机注意如果数据量小且不需要高可用单机或 Sentinel 更简单高效。Cluster 的事务和 Lua 脚本仅支持同一槽位的 Key。三、核心架构原理3.1 哈希槽Hash SlotRedis Cluster没有采用一致性哈希而是使用固定 16384 个哈希槽Hash SlotCRC16(key) % 16384 → 所属槽位每个 Key 通过CRC16算法计算出哈希值对 16384 取模得到槽号每个节点负责一段连续的槽位范围新增/移除节点时只需迁移槽位不需要 rehash 所有数据为什么是 16384 个槽Redis 作者在 FAQ 中解释过16384 个槽用 2KB 的空间即可在节点间传递完整的槽位信息16384 bit ÷ 8 ÷ 1024 2KB而 65535 个槽则需要 8KB在网络开销和灵活性之间取得了平衡。3.2 Gossip 协议Cluster 节点间通过Gossip八卦协议通信每个节点每秒随机选择 5 个节点发送PING交换节点状态、槽位信息、主从关系如果收到PONG超时标记节点为PFAIL可能故障当多数主节点都标记某个节点为PFAIL升级为FAIL并触发故障转移这种去中心化设计意味着没有单点瓶颈但最终一致性意味着短时间内节点间可能存在信息不一致。3.3 主从架构每个主节点Master可以有 1 个或多个从节点Slave主节点负责处理读写请求和槽位数据从节点异步复制主节点数据主节点宕机时选举晋升建议为每个主节点至少配置一个从节点保证高可用四、环境搭建Docker Compose下面我们用 Docker Compose 启动6 个 Redis 节点3 主 3 从这是生产推荐的最小集群配置。version:3.8services:redis-node-1:image:redis:7.2.4container_name:redis-cluster-1command:redis-server--port 6379--cluster-enabled yes--cluster-config-file nodes.conf--cluster-node-timeout 5000--appendonly yes--bind 0.0.0.0ports:-6379:6379-16379:16379networks:-cluster-netredis-node-2:image:redis:7.2.4container_name:redis-cluster-2command:redis-server--port 6379--cluster-enabled yes--cluster-config-file nodes.conf--cluster-node-timeout 5000--appendonly yes--bind 0.0.0.0ports:-6380:6379-16380:16379networks:-cluster-netredis-node-3:image:redis:7.2.4container_name:redis-cluster-3command:redis-server--port 6379--cluster-enabled yes--cluster-config-file nodes.conf--cluster-node-timeout 5000--appendonly yes--bind 0.0.0.0ports:-6381:6379-16381:16379networks:-cluster-netredis-node-4:image:redis:7.2.4container_name:redis-cluster-4command:redis-server--port 6379--cluster-enabled yes--cluster-config-file nodes.conf--cluster-node-timeout 5000--appendonly yes--bind 0.0.0.0ports:-6382:6379-16382:16379networks:-cluster-netredis-node-5:image:redis:7.2.4container_name:redis-cluster-5command:redis-server--port 6379--cluster-enabled yes--cluster-config-file nodes.conf--cluster-node-timeout 5000--appendonly yes--bind 0.0.0.0ports:-6383:6379-16383:16379networks:-cluster-netredis-node-6:image:redis:7.2.4container_name:redis-cluster-6command:redis-server--port 6379--cluster-enabled yes--cluster-config-file nodes.conf--cluster-node-timeout 5000--appendonly yes--bind 0.0.0.0ports:-6384:6379-16384:16379networks:-cluster-netnetworks:cluster-net:driver:bridge配置参数详解参数含义--cluster-enabled yes启用集群模式--cluster-config-file nodes.conf集群自动生成的配置文件不需要手动创建--cluster-node-timeout 5000节点超时时间ms超时被视为故障--appendonly yes开启 AOF 持久化--bind 0.0.0.0允许来自所有网络接口的连接16379端口集群总线端口数据端口 10000用于节点间 Gossip 通信五、创建分片集群5.1 启动容器docker-composeup-d5.2 创建集群进入任意一个容器并执行创建命令# 进入容器dockerexec-itredis-cluster-1sh# 创建集群注意这里用的是容器名不是 IPredis-cli--clustercreate\redis-cluster-1:6379\redis-cluster-2:6379\redis-cluster-3:6379\redis-cluster-4:6379\redis-cluster-5:6379\redis-cluster-6:6379\--cluster-replicas1命令解析--cluster-replicas 1每个主节点分配 1 个从节点总共 6 个节点 → 3 主 3 从如果不加这个参数6 个节点全为主节点不推荐无高可用执行后 Redis 会打印槽位分配方案并让你确认输入yes继续。5.3 参考输出 Performing hash slots allocation on 6 nodes... Master[0] - Slots 0 - 5460 Master[1] - Slots 5461 - 10922 Master[2] - Slots 10923 - 16383 Adding replica redis-cluster-4:6379 to redis-cluster-1:6379 Adding replica redis-cluster-5:6379 to redis-cluster-2:6379 Adding replica redis-cluster-6:6379 to redis-cluster-3:6379 ... [OK] All 16384 slots covered.看到[OK] All 16384 slots covered.表示所有 16384 个槽位分配完毕集群创建成功。六、验证集群状态6.1 查看节点信息dockerexec-itredis-cluster-1 redis-cli cluster nodes输出示例16beb014f42bf997e7e9dc40e4a03d96588ca2f7 172.23.0.5:637916379 master - 0 1784104659539 2 connected 5461-10922 c2c4b89c1359f8b7fc200fb9fb3f5e591132461c 172.23.0.4:637916379 myself,master - 0 1784104658000 1 connected 0-5460 ff93232a5b0ce6abfd4c04dad4e778e47cf19d45 172.23.0.2:637916379 slave 16beb014f42bf997e7e9dc40e4a03d96588ca2f7 0 1784104659000 2 connected d098439d4b4ad4a461777737e7ea2ab2d9591d1b 172.23.0.7:637916379 slave c2c4b89c1359f8b7fc200fb9fb3f5e591132461c 0 1784104659640 1 connected 5089042042bcc6ad2edf0b6dc07d5721c0c4fcf5 172.23.0.6:637916379 slave 7556fed402a74a5dd377365e16d41253d2e1ee99 0 1784104658000 3 connected 7556fed402a74a5dd377365e16d41253d2e1ee99 172.23.0.3:637916379 master - 0 1784104659000 3 connected 10923-16383字段解读字段示例含义节点 ID16beb0...节点唯一标识持久化到 nodes.conf地址172.23.0.5:637916379数据端口集群总线端口角色master/slave主节点或从节点主节点 ID如16beb0...slave 行该从节点所属的主节点连接状态myself,master当前连接的节点是自己槽位范围0-5460该节点负责的哈希槽范围6.2 集群完整性检查dockerexec-itredis-cluster-1 redis-cli--clustercheck redis-cluster-1:6379输出[OK] All nodes agree about slots configuration. Check for open slots... Check slots coverage... [OK] All 16384 slots covered.七、数据读写与 MOVED 重定向7.1 不使用集群模式dockerexec-itredis-cluster-1 redis-clisetfoo bar返回(error) MOVED 12182 172.23.0.3:6379为什么会这样这个错误是 Redis Cluster 的典型行为客户端向redis-cluster-1发送SET foo barRedis 对foo计算哈希槽CRC16(foo) % 16384 12182redis-cluster-1不负责 12182 号槽它负责 0-5460它知道负责该槽的节点是172.23.0.3:6379返回MOVED错误客户端应该重定向到正确节点执行命令7.2 使用集群模式-c 参数# 写入dockerexec-itredis-cluster-1 redis-cli-csetfoo bar# 读取不需要在特定节点读dockerexec-itredis-cluster-3 redis-cli-cget foo-ccluster 模式让redis-cli自动跟随 MOVED 重定向透明地连接到正确的节点执行命令。生产建议应用代码中应使用支持 Cluster 的 Redis 客户端如redis-py-cluster、Lettuce、JedisCluster它们会自动处理 MOVED 重定向和槽位缓存。7.3 查看 Key 的哈希槽dockerexec-itredis-cluster-1 redis-cli CLUSTER KEYSLOT foo# 返回: (integer) 121827.4 查询所有槽位分布dockerexec-itredis-cluster-1 redis-cli CLUSTER SLOTS输出示例1) 1) (integer) 0 -- 起始槽位 2) (integer) 5460 -- 结束槽位 3) 1) 172.23.0.2 -- 主节点 IP 2) (integer) 6379 3) node_id_1 4) 1) 172.23.0.5 -- 从节点 IP 2) (integer) 6379 3) node_id_4 2) 1) (integer) 5461 2) (integer) 10922 3) 1) 172.23.0.3 2) (integer) 6379 ...八、故障转移8.1 手动模拟故障关闭一个主节点观察从节点自动晋升# 停止一个主节点比如 redis-cluster-1它负责 0-5460 槽dockerstop redis-cluster-1# 稍等片刻默认超时 5 秒查看集群状态dockerexec-itredis-cluster-2 redis-cli cluster nodes可以看到原本redis-cluster-1的从节点如172.23.0.2:6379已经晋升为master。8.2 故障转移原理主节点宕机后从节点检测到PING超时从节点等待集群超时时间cluster-node-timeout默认 5 秒通过Raft 类似算法发起选举获得多数主节点投票后晋升为新主节点接管原主节点的所有槽位8.3 恢复原主节点# 重启之前关闭的节点dockerstart redis-cluster-1# 它会以从节点身份重新加入集群成为新主节点的副本九、Hash Tag将关联 Key 存入同一槽位9.1 为什么需要 Hash TagRedis Cluster 的事务MULTI/EXEC和Lua 脚本要求所有操作的 Key必须在同一个哈希槽否则会报错(error) CROSSSLOT Keys in request dont hash to the same slot同时批量操作MGET/MSET跨节点也有额外的网络开销。9.2 Hash Tag 语法Key 中{...}部分只使用花括号内的内容计算哈希槽# 这两个 Key 都会用 user:1 计算哈希槽 {user:1}:name {user:1}:age9.3 验证# 查看两个 Key 的哈希槽注意不加 -c 不会自动重定向但 KEYSLOT 命令不受影响dockerexec-itredis-cluster-1 redis-cli CLUSTER KEYSLOT{user:1}:namedockerexec-itredis-cluster-1 redis-cli CLUSTER KEYSLOT{user:1}:age两个命令返回相同的槽位号。9.4 实际使用# 写入关联 Keydockerexec-itredis-cluster-1 redis-cli-cset{user:1}:name Alicedockerexec-itredis-cluster-1 redis-cli-cset{user:1}:age30# 批量读取在同一槽位效率更高dockerexec-itredis-cluster-1 redis-cli-cmget{user:1}:name{user:1}:age9.5 使用场景场景说明事务操作MULTI/EXEC需要所有 Key 在同一槽位Lua 脚本EVAL脚本中所有 Key 必须在同一槽位批量读写MGET/MSET跨槽位需要多次网络请求关联数据用户信息、订单明细等高关联度数据十、总结与最佳实践关键要点Cluster 适用场景数据量大、写入吞吐高、需要自动故障转移的大规模 Redis 部署最小生产配置6 节点3 主 3 从每个主节点至少一个副本客户端必须支持 Cluster使用-c模式或集成 Cluster 客户端库事务限制MULTI/EXEC 和 Lua 脚本需要配合 Hash Tag{...}节点数建议主节点数量通常为奇数方便选举投票建议 3、5、7 个注意事项网络延迟跨节点操作比单节点慢合理设计 Key 分布批量操作尽量用 Hash Tag 让关联 Key 落在同一槽位扩缩容生产环境扩缩容时关注槽位迁移对性能的影响监控重点关注cluster_state、cluster_slots_ok、cluster_known_nodes等指标
返回列表