为什么需要哨兵和集群?
前面一篇文章中介绍的单机部署架构有两个个问题
- 如果主节点因为故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址
- 单机的内存,CPU,硬盘会限制redis使用
基于哨兵的redis架构能解决第一个问题,基于集群的redis架构能解决这两个问题。
Sentinel(哨兵)
在主节点不可用的情况下,sentinel能自动的完成故障发现和故障转移,并通知应用方,实现高可用。
Sentinel架构
基于下面的架构图我来介绍下redis的sentinel架构。在单机部署架构的基础上增加了sentnel节点,客户端连接的时候不再直接连接主节点而是连接sentinel。整个故障转移的过程为:
- sentinel发现master节点不可用
- 选举一个sentinel节点负责故障转移
- 选择一个从节点执行命令(slaveof no one)变为主节点
- 更新应用方主节点信息
- 从节点去复制新的主节点
- 原来的主节点恢复后去复制新的主节点
sentinel节点的作用
- 监控:定期检查Redis数据节点是否可达
- 通知:故障转移的结果通知给应用方
- 主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系
- 配置提供者:应用方连接sentinel节点,sentinel节点提供主节点信息。
实现原理
sentinel三个定时监控任务
- 每10秒向主节点和从节点发送info命令获取最新拓扑结构
- 每2秒向redis数据节点的一个频道上发送自己的节点信息和对主节点的判断,其他的sentinel订阅后可接受到信息
- 每1秒向主节点和从节点发送ping命令做心跳检测
通过这三个定时任务来连接所有的数据节点和sentinel节点,哪个节点出故障都可以知道。
判断节点不可用
节点对ping命令没有回复,sentinel会认为这个节点主观下线,当多个sentinel节点发现这个节点不可用时会认为这个节点客观下线。判断为客观下线的节点不可用。
sentinel领导者选举
领导者选举算法是使用Raft算法实现的选举算法。算法思路如下:
- 每个sentinel向其他的sentinel发送命令,自己要当领导
- 收到命令的sentinel如果没有同意过其他的sentinel当领导,则同意这个sentinel为领导
- 如果一个节点发现自己的票数大于(节点数/2 + 1),这个节点就为领导
- 一轮没选择出来进入下一轮
故障转移
故障转移的步骤如下:
- 从从节点中选择一个为新的主几点,选择过程中不选择不健康的从节点,选择优先级高的,选择复制偏移量大的。
- 从节点执行slaveof no one命令
- sentinel向其他从节点发送命令,让他们成为新master的从节点
- sentinel把原来的主节点变为从节点并关注,它恢复后变为从节点
Redis Cluster(redis集群)
官方提供了redis集群方案来解决单机部署的资源不足的问题,我们从数据分布,元数据维护方式,集群伸缩,客户端插入key步骤,故障转移几个方面来学习一下。
数据分布
分布式数据库都要解决怎么把整个数据集分布到多个节点的问题,我们来看下redis的数据分布方案。
常见分区方案:
- 哈希分区:离散度好,数据分布与业务无关,无法顺序访问(Redis,Cassandra,Dynamo)
- 顺序分区:离散度不好,数据分布业务相关,可顺序访问(Digtable,Hbase,Hypertable)
一致性哈希分区:
为每个节点在2的32次方的hash环上分配一个数,写入命令的时候,先计算key的hash值(MurmurHash算法),然后顺时针查找大于或者等于这个值的节点。一致性hash分区的缺点:
- 使用的节点少时,节点变化对数据映射影响太大
- 增加或者减少节点的时候需要成倍增加和减少才能保证节点的负载均衡
虚拟槽分区:
redis使用的是在一致性hash分区基础上优化的虚拟槽分区。分区步骤如下:
- 定义redis虚拟槽的范围为0至16383
- redis节点平均分配槽
- 16383个槽平均分配到上面的hash环上
- 计算存入key的hash值,公式 = CRC16(KEY)& 16383
- 找到对应的槽后,根据槽找到对应的redis节点
节点元数据维护方式
- 集中式,节点信息都存放在一个地方,比如RocketMQ集群的Namespace,kafka的zookeeper
- P2P方式,节点信息分布在每个节点,节点间通信来监听节点丢失,节点加入,主从变化,槽信息变化,redis采用这种方式使用Gossip消息来通信
消息分类:
- meet,通知新节点加入
- ping,检测节点是否在线并交换状态信息
- pong,回复响应消息,包含自身状态
- fail,通知节点下线
集群伸缩
集群伸缩复杂的地方在于槽和数据的迁移,数据迁移的过程是一个一个槽来迁移的。
集群扩容步骤
- 准备新节点
- 加入集群
- 迁移槽和数据
集群收缩步骤
- 迁移下线节点负责的槽到其他节点
- forget命令忘记节点
客户端执行命令步骤
正常插入步骤:
- 选择一个运行节点连接
- 缓存槽和节点的映射关系
- 为每个节点创建连接池
- 客户端发送插入命令
- 根据槽和节点映射关系的缓存获取目标节点连接并发送命令
- 出现MOVED重定向,更新槽和节点的映射关系
正在迁移槽的插入步骤:
- 选择一个运行节点连接
- 缓存槽和节点的映射关系
- 为每个节点创建连接池
- 客户端发送插入命令
- 根据槽和节点映射关系的缓存获取目标节点连接并发送命令
- 如果键不存在,执行ASK重定向,使用该节点对应迁移的目标节点的连接再发送命令
故障转移
集群故障转移和sentinel架构下的故障转移原理差不多。
故障发现:
- 主观下线,节点间不断的通信,发现某个节点不可用后,判断这个节点为主观下线的状态
- 客观下线,其他节点收到消息后,如果超过半数的节点标记这个节点主观下线,这个节点就变为客观下线的状态
故障恢复:
- 从节点资格检查,过滤掉与主节点断线时间过长的从节点,
- 准备选举时间,这段时间会根据从节点复制偏移量来定时从节点发起选举的优先级
- 发起选举
- 选举投票,一个从节点收集到半数以上的投票后可执行替换主节点 操作
- 替换主节点,设置选举成功的从节点为主节点,故障节点的槽给自己,广播通知自己变为主节点