简介
Redis Cluster 是 在 3.0 版本正式推出的高可用集群方案,相比Redis Sentinel,Redis Cluster方案不需要额外部署Sentinel集群,而是通过集群内部通信实现集群监控,故障时主从切换;同时,支持内部基于哈希实现数据分片,支持动态水平扩容;某节点宕机只会影响该节点的槽位,不会影响到其他主节点
拓扑
Redis 集群是一个网状结构,无中心结构,每个节点都通过 TCP 连接跟其他每个节点连接。
在一个有 N 个节点的集群中,每个节点都有 N-1 个流出的 TCP 连接,和 N-1 个流入的连接。 这些 TCP 连接会永久保持,并不是按需创建的。
节点们使用一个 gossip 协议来传播集群的信息:发现新的节点、 发送ping包(用来确保所有节点都在正常工作中)、在特定情况发生时发送集群消息。集群连接也用于在集群中发布或订阅消息。
集群中有多个主节点,每个主节点有多个从节点,主从节点间数据一致,最少需要3个主节点,每个主节点最少需要1个从节点
- 高可用:当master节点故障时,自动主从切换
- 高性能:主节点提供读写服务,从节点只读服务,提高系统吞吐量
- 可扩展性:集群的数据分片存储,主节点间数据各不同,各自维护对应数据,可以为集群添加节点进行扩容,也可以下线部分节点进行水平缩容
gossip 协议
原理:所有节点都持有一份元数据,不同的节点如果出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。
gossip 协议包含多种消息,包含 ping,pong,meet,fail 等等
- meet:某个节点发送 meet 给新加入的节点,让新节点加入集群中,然后新节点就会开始与其它节点进行通信。
- ping:每个节点都会频繁给其它节点发送 ping,其中包含自己的状态还有自己维护的集群元数据,互相通过 ping 交换元数据。
- pong:返回 ping 和 meeet,包含自己的状态和其它信息,也用于信息广播和更新。
- fail:某个节点判断另一个节点 fail 之后,就发送 fail 给其它节点,通知其它节点说,某个节点宕机
数据分区
槽是 Redis 集群管理数据的基本单位
redis cluster 有固定的 16384 个 hash slot,对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的 hash slot,利用链表解决哈希冲突
公式:HASH_SLOT = CRC16(key) mod 16384
redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。
每个集群节点维护着一个16384 bit (2KB)的位数组,每个bit对应相同编号的槽,用 0 / 1标识对于某个槽自己是否拥有
集群节点同时还维护着槽到集群节点的映射,是由长度为16384,数组下标代表槽编号,值为节点信息的数组
作者之所以设计16384个槽位,主要原因就是集群节点间每次ping都需要发送自己2KB的槽位数组,设置的槽位太大会增加传输消耗
hash tag
默认情况下,key在哪个slot上由key进行哈希后的结果决定,但有的时候我们希望把同一批相关的key存放到同一个slot上,提升性能与保证原子性,这时就可以利用hash tag。
假设一个key是9999:order,表示用户ID为9999的订单信息,只需要用{}将key中我们需要进行哈希计算那部分包起来,即{9999}:order;另外其他的{9999}:user同样可以这样操作
这样就将同一个用户的所有相关信息都放到了同一个哈希槽中
高可用
- 判断节点宕机:如果一个节点认为某个节点 pfail 了,那么会在 gossip ping 消息中,ping 给其他节点,如果超过半数的节点都认为 pfail 了,那么就会变成 fail
- 从节点选举:每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票,如果大部分 master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成 master。
从节点执行主备切换,从节点切换为主节点。
集群客户端
基于重定向的客户端,很消耗网络IO,因为大部分情况下,可能都会出现一次请求重定向,才能找到正确的节点。
JedisCluster初始化时会随机选择一个节点获取集群元数据,初始化hash slot对应jedisPool的Map映射表,后续的操作如下,无需通过集群的重定向来执行命令:
- 把key作为参数,执行CRC16算法,获取key对应的slot值
- 通过该slot值,去slots的map集合中获取jedisPool实例
- 通过jedisPool实例获取jedis实例,最终完成redis数据存取工作