1. 简介

Redis Cluster(集群)是 Redis 官方提供的分布式解决方案,通过数据分片和高可用机制,解决单机 Redis 在存储容量、性能和可靠性上的限制。

Redis Cluster 是一种无中心化的分布式架构,通过将数据分散到多个节点(分片),实现水平扩展,并且支持主从复制和自动故障转移恢复,具有高可用性。

它适用于大规模数据存储和高并发访问的场景,如电商秒杀、实时数据缓存等场景。

2. 架构

2.1 数据分区

分布式数据库需要把整个数据集按照分区规则,映射到多个节点上,每个节点负责整体数据的一个子集。

一般采用哈希分区规则,将数据分到各个节点上,常见的哈希分区有以下几种:

节点取余分区

根据节点数量 N,使用公式 hash(key) % N,计算出哈希值后取余,决定数据映射到哪个节点上。

这个方式存在的问题是,当需要扩容或收缩节点,节点数量变化,数据节点映射关系需要重新计算,导致数据重新迁移。这个方式的优点是简单,常用于数据库的分库分表规则,需要预先按数据量规划好分区数量,以支撑未来一段时间的数据量。扩容时通常将节点翻倍,避免数据映射被打乱导致的全量迁移,但也还是要迁移 50% 数据。

一致性哈希分区

为系统中每个节点分配一个token,范围在 0 - 2^32,这些 token 构成一个哈希环。数据读写时查找节点,先根据 key 计算哈希值,再顺时针找到第一个大于等于该哈希值的 token 节点。

这个方式的好处是添加删除节点只影响哈希环中相邻的节点,对其他节点无影响。但也有一些问题,加减节点会造成部分数据无法命中,需要手动处理或忽略,使用少量节点时节点变化会大范围影响数据映射,这种方式不适合少量数据节点的方案。

虚拟槽分区

使用哈希函数将所有数据映射到固定范围的整数集合中,整数定义为槽(slot),作为数据管理和迁移的基本单位,一般远大于节点数,每个节点负责部分槽。

Redis Cluster 使用虚拟槽分区,所有键根据 CRC16 算法哈希映射到 0 - 16383 整数槽,计算公式为 slot = CRC16(key) % 16383,每个节点负责维护一部分槽及其对应的数据。

这个设计解耦了数据和节点之间的关系,简化了节点扩展收缩的难度。节点自身维护槽的映射关系,支持节点、槽、键的映射查询,可用于数据路由、在线伸缩等场景。

这也使得 Redis Cluster 相对于单机存在一些功能限制:

  • key 批量操作支持有限,只支持相同 slot 的 key 执行批量操作,mset、mget 可能因为数据分布在不同节点而不被支持;
  • key 事务操作支持有限,多个 key 分布在不同节点时无法使用事务;
  • key 是数据分区的最小粒度,不能将一个大的键值对象拆分映射到不同节点上;
  • 不支持多数据库空间,只有一个数据库空间 db 0;
  • 复制结构只支持一层,不支持嵌套的树状复制结构;

2.2 节点通信

Redis Cluster 采用 gossip 协议进行节点间的通信,不断交换信息,一段时间后所有节点都会知道集群的完整信息。

集群中的每个节点都会单独开辟一个 TCP 通道用于通信,通信端口号等于基础端口加 10000。每个节点会定时选择几个节点发送 ping 消息,收到的节点用 pong 消息作为响应。

消息类型有:

  • meet 消息:通知新节点加入;
  • ping 消息:检测节点是否在线和交换状态信息;
  • pong 消息:回复响应,并封装自身状态数据;
  • fail 消息:节点判定另一节点下线,会在集群内广播 fail 消息,其他节点收到后把对应节点更新为下线状态;

2.3 集群伸缩

Redis 集群中的主节点分别负责一部分的槽。

当有新节点时,首先将节点加入现有集群中。加入的节点还没有分配槽,可以为它迁移槽以实现集群扩容,或者作为其他主节点的从节点负责故障转移。

对于集群中的现有节点,也可以将其负责的槽迁移到其他节点,然后将该节点下线,以实现集群收缩。

2.4 请求路由

在集群模式下,Redis 接受任何键的命令时,首先计算键对应的槽,再根据槽找出所对应的节点。如果节点是自身,则处理该命令,否则回复 MOVED 重定向错误,通知客户端请求正确的节点。

重定向信息包含键对应的槽和负责该槽的节点地址,客户端就可以向正确的节点重新发起请求。

redis-cli 命令加入 -c 参数时,支持自动重定向。

3. 搭建集群

Redis Cluster 需要至少 6 个节点才能保证组成完整高可用的集群,每个节点需要开启配置 cluster-enabled yes

配置文件,多个节点配置的区别在于端口号:

port 6379
cluster-enabled yes
cluster-node-timeout 15000
cluster-config-file "node-6379.conf"

分别启动节点:

redis-server node-6379.conf
redis-server node-6380.conf
redis-server node-6381.conf
redis-server node-6382.conf
redis-server node-6383.conf
redis-server node-6384.conf

Redis 会生成一份集群配置文件并自动维护,不能手动修改。当集群内出现节点添加、节点下线、故障转移时,会自动保存集群状态到配置文件。

节点握手

登陆客户端向任意另外节点发起节点握手,集群中的节点会通过 gossip 协议彼此通信并感知对方,自动发起握手流程。

cluster meed <ip> <port>

此时集群还处于下线状态,无法进行数据读写。

获取集群状态:

cluster info

查看节点ID、地址、槽的分配关系:

cluster nodes

分配槽

槽的数量固定的,通过命令将槽分配给 3 个节点:

redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5462..10922}
redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923..16383}

此时集群状态变为 OK,进入在线状态,所有槽都已经分配给节点。

对另外 3 个节点设置为前 3 个节点的从节点:

redis-cli -h 127.0.0.1 -p 6382 cluster replicate cfb28ef
redis-cli -h 127.0.0.1 -p 6383 cluster replicate 8e41673
redis-cli -h 127.0.0.1 -p 6384 cluster replicate 40b8d09

3. 对比 Sentinel

Redis Cluster 和 Redis Sentinel 是 Redis 提供的两种不同架构的高可用性解决方案,他们在功能特性和适用场景上有所不同。

Redis Cluster 包含多个主节点和从节点。通过内置的主从复制实现了高可用性,通过 16384 个哈希槽的数据分片实现了水平扩展能力,支持分布式存储和负载均衡。数据分散在多个主节点上,还能通过从节点作为副本,提供故障转移的能力。适用于海量数据、高并发读写、需要突破单机性能和内存瓶颈的场景。

Redis Sentinel 包含主从节点以及独立的 Sentinel 节点。通过主从架构实现了高可用性,并且支持自动故障转移。每个主从节点都存储全量数据,不支持数据分片。适用于中小规模数据、读写分离、无需水平扩展的场景。