从零开始认识 Redis Cluster:基础介绍 – wiki基地


从零开始认识 Redis Cluster:基础介绍

引言:单机 Redis 的困境

Redis 作为一款高性能的键值存储系统,凭借其极快的读写速度和丰富的数据结构,在现代互联网应用中占据了举足轻重的地位。然而,随着业务规模的不断扩大,单台 Redis 服务器所面临的挑战也日益凸显:

  1. 内存限制 (Capacity Limit): 单台服务器的物理内存总是有限的。当存储的数据量超过单机内存容量时,单机 Redis 就无法满足需求。
  2. 处理能力限制 (Throughput Limit): 即使数据量不大,高并发的读写请求也可能达到单机 Redis 的处理瓶颈(CPU、网络)。
  3. 单点故障 (Single Point of Failure – SPOF): 一旦这台 Redis 服务器发生故障(硬件损坏、网络中断、进程崩溃等),整个服务将不可用,造成严重影响。

为了克服这些限制,业界发展出了多种 Redis 的高可用和扩展方案,例如主从复制 (Replication)、哨兵模式 (Sentinel) 和集群模式 (Cluster)。主从复制解决了读写分离和部分高可用问题(通过手动或脚本切换),哨兵模式在主从复制的基础上提供了自动故障转移,极大地提升了高可用性。

然而,无论是主从复制还是哨兵模式,它们的核心存储能力仍然集中在一个主节点上。它们解决了高可用性问题,但并没有解决数据量和处理能力随着规模扩大而增长的问题。当数据量或并发量达到单机极限时,这些方案就显得力不从心了。

这时,我们就需要一种能够水平扩展 (Horizontal Scaling) 的方案,能够将数据分散存储到多台服务器上,共同承担读写压力。Redis Cluster 应运而生,它正是 Redis 官方提供的分布式解决方案,旨在解决单机 Redis 的容量、处理能力和高可用性瓶颈。

什么是 Redis Cluster?

Redis Cluster 是 Redis 官方提供的分布式、高可用解决方案。它在设计上是去中心化 (Decentralized) 的,不依赖于外部的协调服务(如 ZooKeeper、Etcd)。集群中的每个节点都相互连接,并通过 Gossip 协议交换信息。

Redis Cluster 的主要目标是:

  1. 数据自动分片 (Automatic Sharding): 将海量数据自动分散到多个节点上,突破单机内存限制。
  2. 提供一定的高可用性 (High Availability): 当部分节点发生故障时,集群仍然能够继续对外提供服务。

简单来说,Redis Cluster 就是将多个 Redis 实例组织起来,让它们共同对外提供服务,并且能够在部分节点失效时自动进行故障转移,保证服务的连续性。

Redis Cluster 的核心概念

理解 Redis Cluster,需要掌握以下几个核心概念:

  1. 节点 (Node): 集群由多个 Redis 节点组成。每个节点都是一个独立的 Redis 实例。这些节点可以是主节点 (Master),也可以是从节点 (Replica)。
  2. 哈希槽 (Hash Slot): 这是 Redis Cluster 数据分片的核心机制。整个集群的键空间被分割成 16384 个哈希槽 (slot)。集群中的每个主节点都负责管理一部分哈希槽。
  3. 键到哈希槽的映射 (Key to Slot Mapping): Redis Cluster 使用固定的算法计算一个键属于哪个哈希槽:slot = CRC16(key) % 16384。其中,CRC16 是一个标准的校验和算法。这意味着任何一个键都会确定地映射到一个 0 到 16383 之间的整数,也就是对应的哈希槽。
  4. 槽的分配 (Slot Distribution): 在集群启动和运行过程中,这 16384 个哈希槽会分配给集群中的主节点。每个主节点负责维护并处理其被分配到的所有哈希槽中的数据。例如,一个由 3 个主节点组成的集群,节点 A 可能负责槽 0-5460,节点 B 负责 5461-10922,节点 C 负责 10923-16383。这种分配是动态的,可以在集群运行时通过重分片 (resharding) 进行调整。
  5. 主从复制 (Replication): 为了提供高可用性,Redis Cluster 支持为每个主节点配置一个或多个从节点。从节点复制主节点的数据。当主节点故障时,集群会从其从节点中选举一个升级为新的主节点。
  6. 集群总线 (Cluster Bus): 集群节点之间通过一个特殊的 TCP 端口进行通信,这个端口比普通客户端服务端口大 10000。例如,如果一个节点通过 6379 端口服务客户端请求,它会通过 16379 端口与集群中的其他节点进行通信,交换集群状态信息、传播命令等。
  7. Gossip 协议: 节点之间通过集群总线使用 Gossip 协议交换集群的元数据信息,包括节点的健康状态、负责的哈希槽信息、主从关系等。这使得每个节点最终都能了解到整个集群的状态,而无需一个中心化的控制节点。

数据分片:哈希槽详解 (Hash Slots)

哈希槽是 Redis Cluster 最为关键的设计之一。理解了哈希槽,就理解了 Redis Cluster 如何进行数据分片。

为什么是 16384 个槽?

Redis 官方文档解释选择 16384 个槽的原因是基于性能和节点数量的权衡。

  • 槽数量太少: 如果槽数量很少(比如 256 个),虽然每个节点维护的槽信息更少,但在 Gossip 协议中,每个 PING 包需要携带其他节点负责的槽信息。如果槽数量太多,Gossip 消息会变得非常大,消耗带宽。
  • 槽数量太多: 如果槽数量非常大,例如 16384 * 10 (163840),虽然可以支持理论上更多的节点(每个节点即使只负责一个槽),但每个节点需要维护的槽分配信息(一个位图,每个位代表一个槽是否由该节点负责)会显著增加内存开销。16384 个槽,对应的位图是 16384/8 = 2048 字节,大约 2KB,这对于每个节点来说开销很小。
  • 16384 的选择: 16384 (2^14) 是一个折衷的选择。它既保证了 Gossip 消息的大小在一个可接受的范围内(每个节点 PING 包发送 Slot 信息时,用一个 2KB 的位图表示自己负责的槽),又允许集群支持相当数量的节点(即使有上千个节点,每个节点也能分到足够的槽,避免出现某些节点只负责少量槽而导致数据或流量不均的情况)。虽然理论上 Redis Cluster 可以支持 1000 个节点,但官方建议的集群规模在几十到上百个节点之间。

键到哈希槽的映射过程

当客户端向 Redis Cluster 发送一个命令(如 SET mykey "hello")时,集群节点需要知道 mykey 这个键应该由哪个节点处理。这个过程就是通过哈希槽完成的:

  1. 计算键的哈希值:使用 CRC16 算法计算键 mykey 的校验和。
  2. 取模:将 CRC16 的计算结果对 16384 取模,得到一个介于 0 到 16383 之间的整数。例如 CRC16("mykey") % 16384 可能结果是 1234。
  3. 定位槽归属节点:集群的每个节点都知道哪个主节点负责槽 1234。
  4. 请求路由:客户端的请求会被路由到负责槽 1234 的主节点上进行处理。

Hash Tags ({...})

有时候,我们希望多个不同的键能够强制分配到同一个哈希槽中。这在执行涉及到多个键的原子操作(如 MGET, MSET, SUNION, 事务, Lua脚本)时非常有用。Redis Cluster 默认只保证单键操作的原子性。对于多键操作,如果这些键分布在不同的槽中,集群将无法直接执行,会返回错误。

为了解决这个问题,Redis Cluster 引入了 Hash Tags 的概念。如果一个键包含 {} 字符,那么只有 {} 之间的字符串会被用于计算哈希槽。例如:

  • {user1}.profile
  • {user1}.orders
  • {user1}.cart

这三个键虽然不同,但它们都包含 {user1}。计算哈希槽时,只会使用 user1 这个字符串:CRC16("user1") % 16384。这样,这三个键就一定会被分配到同一个哈希槽,从而由同一个主节点处理,使得在这些键上执行多键操作成为可能(前提是客户端能将命令正确地路由到该节点)。

注意: 使用 Hash Tags 可能会导致槽的分配不均匀,如果大量键都使用相同的 Hash Tag,会导致某个槽的数据量和访问压力非常大,形成热点槽,影响集群的整体性能和平衡。因此,应谨慎使用 Hash Tags,并在必要时进行数据模型的调整。

高可用性:主从复制与故障转移

数据分片解决了容量和吞吐量问题,但如果负责某个槽的主节点故障了怎么办?这就需要高可用机制。Redis Cluster 通过主从复制和自动故障转移来实现高可用性。

主从复制

在 Redis Cluster 中,可以为每个主节点配置一个或多个从节点。这些从节点复制主节点的数据,保持与主节点的数据同步。

  • 配置: 在创建或加入集群时,可以将某个节点指定为另一个主节点的从节点。例如,redis-cli --cluster replicate <replica-node-id> <master-node-id>.
  • 作用: 从节点的主要作用是作为其主节点的备份。当主节点下线时,从节点可以接替它继续提供服务。

自动故障转移 (Automatic Failover)

当集群检测到某个主节点下线时,会自动进行故障转移过程,使得该主节点负责的槽能够继续对外服务。这个过程大致如下:

  1. 故障检测 (Failure Detection): 集群中的每个节点都会定期通过 Gossip 协议向其他节点发送 PING 消息,并接收 PONG 消息。如果一个节点长时间没有收到某个节点(假设是节点 A)的 PONG 回复,它会标记节点 A 为 PFAIL (Possible Failure)。当集群中大多数的主节点都标记节点 A 为 PFAIL 时,它们会通过 Gossip 协议广播消息,将节点 A 的状态标记为 FAIL (Failure)。FAIL 状态表示集群确认该主节点已经下线。
  2. 故障转移启动 (Failover Initiation): 当一个主节点被标记为 FAIL 后,其对应的从节点会收到这个信息。这些从节点中的一个会启动故障转移流程。
  3. 选举新的主节点 (Electing a New Master): 多个从节点可能会同时尝试进行故障转移。它们会向集群中的其他主节点发送 FAILOVER_AUTH_REQUEST 请求,希望获得投票。拥有投票权的是集群中的主节点。当一个从节点收到了大多数主节点的投票后,它就赢得了选举,可以升级为新的主节点。这个选举过程使用了类似 Raft 或 Paxos 的领袖选举算法的简化版本。
  4. 从节点升级 (Replica Promotion): 赢得选举的从节点执行 SLAVEOF NO ONE 命令,变成一个主节点。
  5. 广播状态 (State Broadcasting): 新的主节点会向集群中的其他节点发送消息,告知它们自己现在是某个哈希槽范围的新主节点。其他节点更新它们内部的哈希槽到节点映射信息。
  6. 客户端重定向 (Client Redirection): 当客户端连接到旧的主节点或一个不知道状态更新的节点时,会收到 MOVED 重定向响应,指引客户端连接到新的主节点。

容错能力 (Fault Tolerance):

Redis Cluster 的容错能力取决于每个主节点的从节点数量以及集群的节点数量。

  • 如果没有从节点,任何主节点故障都会导致其负责的哈希槽不可用,集群将无法提供完整服务。
  • 如果每个主节点至少有一个从节点,那么即使某个主节点故障,其从节点可以升级为新的主节点,服务可以继续。
  • 集群的故障转移需要大多数主节点的投票。这意味着,如果集群中超过一半的主节点同时故障,集群将无法进行有效的故障转移,整个集群可能会停止服务。例如,一个 6 个主节点(每个主节点带 1 个从节点,总共 12 个节点)的集群,如果同时有 4 个主节点故障,剩下的 2 个主节点无法形成多数,故障转移将失败。因此,部署时需要合理规划节点数量和分布,以避免这种情况。

节点通信:集群总线与 Gossip 协议

Redis Cluster 的去中心化架构依赖于节点之间的 P2P 通信。这种通信通过集群总线和 Gossip 协议实现。

集群总线 (Cluster Bus)

每个 Redis Cluster 节点会监听两个端口:

  • 客户端服务端口: 用于处理来自客户端的命令,例如 6379。
  • 集群总线端口: 用于节点之间的通信,通常是客户端服务端口 + 10000,例如 16379。

节点之间通过集群总线端口发送和接收消息,这些消息是特殊的二进制协议,用于交换集群状态、故障信息、配置更新等。

Gossip 协议

Redis Cluster 使用 Gossip 协议进行节点之间的状态同步。这是一种去中心化的分布式协议,其特点是每个节点都随机地与其他节点交换信息。经过一段时间,信息就会像病毒传播一样扩散到整个集群。

Gossip 协议在 Redis Cluster 中主要用于:

  • 节点发现 (Node Discovery): 新加入的节点通过 MEET 消息被其他节点认识。
  • 状态传播 (State Propagation): 节点之间交换彼此的状态信息,包括是否在线、负责哪些哈希槽、主从关系等。
  • 故障检测 (Failure Detection): 通过定期 PING/PONG 和交换状态信息,节点可以发现其他节点的不可达状态,并传播这些信息。
  • 配置更新传播 (Configuration Updates): 当哈希槽分配发生变化(如重分片、故障转移)时,这些信息通过 Gossip 协议传播到所有节点,使它们更新自己的内部配置。

Gossip 协议的优点是去中心化、鲁棒性强(没有单点故障),但缺点是信息传播有延迟,且可能消耗一定的带宽。

客户端如何与 Redis Cluster 交互?

与单机 Redis 不同,客户端不能随意连接到集群中的任何节点并期望所有命令都能成功执行。由于数据是分片的,一个键可能存储在集群中的任意一个主节点上。

Redis Cluster 客户端必须是集群感知 (Cluster-aware) 的客户端。这意味着客户端需要能够理解 Redis Cluster 的协议和重定向机制。

交互流程大致如下:

  1. 初始连接: 客户端连接到集群中的任意一个节点(这个节点不一定是目标键所在的节点)。
  2. 发送命令: 客户端发送命令,例如 GET mykey
  3. 节点处理与重定向:
    • 如果接收命令的节点恰好是负责 mykey 所在哈希槽的主节点,它会直接处理命令并返回结果。
    • 如果接收命令的节点不是负责 mykey 所在哈希槽的主节点,它会计算 mykey 属于哪个哈希槽,然后查找该哈希槽当前归属哪个节点。然后,它不会处理这个命令,而是向客户端返回一个重定向错误
      • MOVED <slot> <ip:port>: 这个错误表示槽 <slot> 已经永久地迁移到了 <ip:port> 这个节点。客户端收到 MOVED 错误后,应该更新自己内部维护的哈希槽到节点映射表,将 <slot> 标记为由 <ip:port> 负责,然后重新连接<ip:port> 节点并重新发送原始命令。
      • ASK <slot> <ip:port>: 这个错误表示槽 <slot> 当前正在从当前节点迁移<ip:port> 节点。客户端收到 ASK 错误后,不应更新其哈希槽映射表,而只是临时地连接到 <ip:port> 节点,先发送一个 ASKING 命令,然后再发送原始命令。ASKING 命令会使目标节点接受一次针对正在迁移槽的命令,即使客户端没有被正式授权处理这个槽。ASK 重定向是临时的,用于处理槽迁移过程中的客户端请求。
  4. 客户端更新映射表: 智能客户端会缓存哈希槽与节点之间的映射关系。当收到 MOVED 错误时,客户端会更新这个缓存,以便下次可以直接将属于该槽的命令发送到正确的节点,减少重定向的开销。

重要的点:

  • 客户端不需要连接到集群中的所有节点,只需要连接到其中几个即可发现整个集群拓扑(通过 CLUSTER SLOTSCLUSTER NODES 命令)。
  • 一个好的集群感知客户端会维护一个哈希槽到节点的映射表,并在收到 MOVED 重定向时更新它,从而提高后续操作的效率。
  • 对于多键命令,客户端需要先计算所有键的哈希槽,如果它们不在同一个槽中,并且没有使用 Hash Tags,客户端通常会拒绝执行或由集群节点拒绝执行。

集群的基本操作(高层介绍)

虽然详细的集群操作(如创建、添加/删除节点、重分片)涉及一系列步骤和命令,但从基础介绍层面,了解这些操作的目的即可:

  1. 集群创建 (redis-cli --cluster create ...): 使用 redis-cli 工具可以方便地创建一个新的 Redis Cluster。你需要指定至少 3 个主节点(为了保证大多数节点存活时能进行选举),通常为了高可用性,还会为每个主节点指定从节点。工具会自动分配哈希槽,并将从节点关联到主节点。
  2. 添加节点 (redis-cli --cluster add-node ...): 可以向现有集群添加新的节点,可以添加新的主节点(需要随后进行重分片将一些哈希槽迁移到新主节点上)或作为现有主节点的从节点。
  3. 删除节点 (redis-cli --cluster del-node ...): 可以从集群中删除一个节点。如果删除的是主节点,需要先将该主节点负责的哈希槽迁移到其他节点上,或者如果该主节点没有从节点,先为其添加从节点并等待故障转移完成。
  4. 重分片 (redis-cli --cluster reshard ...): 这是集群扩展或平衡数据分布的关键操作。通过重分片,可以将一部分哈希槽从一个或多个源节点迁移到一个或多个目标节点。这个过程是在线进行的,集群在迁移过程中仍然可以处理客户端请求(尽管涉及迁移槽的请求可能会有短暂的 ASK 重定向)。
  5. 添加从节点 (redis-cli --cluster replicate ...): 为已有的主节点添加新的从节点,提高其高可用性。

这些操作通常由 redis-cli --cluster 工具简化和自动化,降低了管理的复杂性,但底层原理依然是基于哈希槽的重新分配和数据迁移。

Redis Cluster 的优缺点

优点:

  1. 水平扩展: 能够突破单机内存和吞吐量的限制,支持海量数据和高并发。
  2. 高可用性: 自动故障转移机制保证了在部分节点故障时服务的连续性。
  3. 去中心化: 没有中心化的协调服务依赖,架构相对简洁,避免了协调服务的单点故障(尽管管理维护复杂性提高)。
  4. 性能: 数据分散在多个节点上,可以利用多台服务器的 CPU 和网络资源,总体的读写性能可以大幅提升。

缺点:

  1. 多键操作限制: 对于分布在不同哈希槽的键,无法进行原子性的多键操作(事务、Lua脚本、MGET/MSET等),除非使用 Hash Tags,但这可能导致数据分布不均。
  2. 管理和运维复杂性: 相比单机或主从+哨兵模式,集群的搭建、监控、维护、扩展和缩容更加复杂。需要关注每个节点的运行状态、槽的分配、主从关系等。
  3. 客户端限制: 需要使用集群感知客户端。
  4. 部分命令不支持: 例如,与数据库相关的命令(如 SELECTFLUSHALL 在集群中是全局的,但慎用)以及一些特定于单实例的命令在集群环境中行为可能不同或受限。不支持多数据库功能。

总结

Redis Cluster 是 Redis 官方为解决单机性能和容量瓶颈而提供的分布式解决方案。它通过将键空间划分为 16384 个哈希槽,并将这些槽分配给集群中的多个主节点来实现数据分片。结合主从复制和去中心化的 Gossip 协议,Redis Cluster 提供了自动故障转移能力,保证了集群的高可用性。

从零开始认识 Redis Cluster,最重要的是理解其核心思想:数据分片是基础,哈希槽是实现分片的机制,主从复制是保障高可用性的手段,节点间的 Gossip 协议是实现去中心化管理和状态同步的关键,而客户端的智能重定向是与集群正确交互的前提。

虽然 Redis Cluster 引入了一些管理和操作上的复杂性,并且对多键操作有限制,但对于需要处理大规模数据集和高并发访问场景的应用程序来说,Redis Cluster 提供了强大的水平扩展和高可用能力,是构建高可用、高性能 Redis 服务的首选方案。

了解了这些基础概念,你就可以进一步学习如何搭建一个 Redis Cluster,如何进行节点的增删、数据的重分片,以及如何监控和维护一个运行中的 Redis Cluster 了。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部