Redis的哨兵和集群
[TOC]
哨兵模式
什么是哨兵模式?
它是Redis的一种高可用架构,遵循CAP原则中的CP原则。我们在这里其实就可以把它看作为一个Zookeeper的集群,一般由3个以上的节点组成。它会监控所有的节点,当主节点挂掉的时候,它会选举一个最优的从节点升级为主节点。当我们在用client去连接的时候,它首先去连接Sentinel节点,然后通过Sentinel节点去查到主节点地址,再去和主节点进行数据交互。如果主节点挂掉了,Sentinel节点就会进行选取,将一个最优从节点升级为主节点,再把新的主节点的IP:Port通知给client.
为了改进主从模式下必须得要去调整主从节点。因为在主从模式下,如果主节点出现问题了,就必须得要手动的调节,首先应该把其他的从节点和之前的主节点的配置文件中将slave of 换成新的主节点的IP:Port,再把新的主节点的slave of 清除。
对于哨兵模式中,它里面包含了Sentinel节点和数据节点。Sentinel节点不会去存储数据,它只会监控数据节点和除了他之外的其他Sentinel节点。如果发现节点没有消息传过来之后就会下线这个节点,如果这个节点是主节点就会与其他的Sentinel节点进行协商,如果大部分的Sentinel节点觉得这个主节点不可达之后就会下线这个节点,然后推选出一个Sentinel节点作为大哨兵去完成故障自动迁移的操作,然后将这个变化通知给应用放。Sentinel节点必须是奇数,而且数量>2。(这里和ZK比较相似)
哨兵模式的小细节
哨兵是如何去监控这些节点的
前面说了Sentinel节点监控数据节点和除了他之外的的Sentinel节点,那么它是通过什么来获得各个节点的发现和监控?它这里有三种定时任务去达成:
- 每隔10s,哨兵会向数据节点发送info指令,从而获取数据节点的拓扑结构。这是为了去获取master和slave的状态,也可以去检索其他新加入的从节点,如果哪个节点出了问题,就会比较及时的获取到状态。
- 每隔2s,哨兵会向Redis数据节点的sentinel: hello 频道发送 当前哨兵节点的信息还有对主节点的判断。因为每一个Sentinel节点都必须订阅这个频道,这样的话有新的Sentinel节点加入进来的话,其他的Sentinel节点都会感知到,也可以通过这个频道来交流。这样的话 Sentinel节点就可以在这里来客观下线和选举Leader.
- 每隔1s,当前哨兵节点会向其他的哨兵节点和数据节点发送ping命令,看是否得到回复.这样也是为了监控每个节点,以此来发现这些节点是否出现问题。
哨兵模式的下线模式
我们刚刚在定时任务中讲了 Redis在Sentinel:hello频道中交流客观下线和选举leader,下线分为两种
- 主观下线:在哨兵向其他的节点发送ping命令之后,如果有节点没在规定的时间内获取到回复的话,哨兵节点就会认为这些节点已经下线.默认时间为30s
- 客观下线:如果哨兵下线的是主节点的话,就会和其他的哨兵去发送对主节点的判断,如果有一半以上的哨兵觉得这个主节点是有问题的话,就会认为这个主节点是客观下线。所以说,客观下线是针对于主节点的。
哨兵模式的选举
如果哨兵对这个主节点认为客观下线的话,就会进行哨兵选举,最后选举出一个哨兵队长去进行一个故障转移的操作。Redis的选举算法是根据Paxos算法改进的一个名叫Raft的选举算法,如果想要了解这个算法的内容的话就去下面的这个网址里看看,这里不做详细介绍。raft算法
故障迁移:先过滤掉被主观下线的从节点,如果有从节点优先级的话就选最先的,如果没有的话就选偏移量最大的(偏移量越大,复制的约完整)如果还没选举出就选择runid(每个redis启动都会获得一个随机的id)最小的。
消息丢失
如果主节点挂掉了,从节点还没来得及收到全部的同步信息,那么这中间的没有接收到的信息就会全部丢失了,但是Sentinel模式为了尽可能地去减少丢失的信息,他通过下面的两种参数来保证。
- min-slaves-to-write 1
- min-slaves-max-lag 10
第一个参数就是保证主节点至少有一个从节点进行正常的复制,否则的话就不会对外提供服务。从这里就可以体现出 CAP原则中舍弃了A选择了CP。
第二个参数是为了鉴定从节点是否在进行正常的复制,如果10s之内收到slave节点的反馈就会认为这个从节点是正常复制,反之则是异常复制。
Cluster模式
什么是cluster模式?
这是一种去中心化的一种集群模式,在CAP原则中遵循了AP原则。由Redis作者自己提供的一种集群化方案。在Cluster中,每个节点负责一部分数据,然后这些节点互相连接,通过Gossip协议来进行通信。
Cluster的数据分布理论并没有运用到我们常见的一致性Hash理论和节点对分区取余这两种方式。它用了一种比较少见的虚拟槽分区法来进行分区。Cluster将这些数据划分在16383(2^14 -1)个slots中。slots就是集群内的数据迁移和管理的基本单位了。
key存储到某个槽的计算公式是: slot = CRC16(key) %16383
假如这个集群中有5个结点的话,那么这5个节点就会均分这16383个slot。
cluster模式的缺点
- 无法进行批量操作,如mget、mset等操作。因为这些key可能会被映射到不同的slot中。
- 无法支持事务,对于在同一个slot中的key可以支持事务,如果这些key不在同一个slot中,就无法去进行事务操作。
- 容易引起大对象的问题,因为key是slot的基本单位,如果这个key的数据过于大就会引起大对象问题。
- 只有一个数据库空间,只有一个db0
cluster的启动流程
-
启动。在我们的redis启动的时候,它会先在配置文件中查看是否开启集群模式,如果开启了集群模式的话再去看是否有了集群配置文件,如果有的话就使用集群配置文件来进行启动,如果没有的话,就会创建一种默认的集群配置文件,然后再进行启动。
-
握手。在集群启动之后,他们会通过Gossip协议来进行通信,这样就可以去感知到对方了。
-
槽分配。在握手之后,整个集群并没有真正的处于上线状态,你可以用info指令测试一下,就会发现整个集群还是处于一种下线状态。因为它并没有进行槽的分配,只有我们把槽分配给这些节点之后才会让集群上线。cluster addslots这条命令可以用来分配槽位。
Gossip协议
Gossip协议是Redis的Cluster中节点与节点之间通信用的协议,这是一种P2P通信模式的协议,就好像A告诉B一些悄悄话,B再去告诉C,C再去告诉D,这种扩散方式因为特别像流言的扩散方式,所以这种协议也被称作为流言协议。这种协议通过节点之间创建的TCP通道来进行通信的,然后在固定周期内通过特定的规则选择几个节点发送ping,收到的节点则回复pong
上段话讲过了,节点通过一定的规则去选择几个节点发送ping,所以 这里的每个节点可能知道所有的节点也可能知道部分节点,只要这些节点能够通信,他们最终就会达到一种一致性的状态。这种一致性就被称为最终一致性。我们也可以在这里看出 Cluster中对AP原则的应用。
Gossip协议的消息
这里的消息被分为四种消息,每种消息都有自己的特性
- ping,这是集群中节点之间交换最为频繁的一种消息。它一般被用来检测节点是否在线,交换彼此的信息
- pong,当收到了ping消息或meet消息之后,都得要去回复pong消息来表示自己可以正常通信。
- meet,当有新节点加入的时候就会用到meet消息
- fail,当有节点判断另外一个节点下线的时候,就会广播fail消息,收到fail消息的系欸但就会把这个节点标记为下线节点。
集群的伸缩
前面说过,集群的扩容和收缩相比哨兵模式来说是比较节省资源而且效率比较高的。因为我只需要把这些slot均分在各个节点中就可以了,如果需要增加节点就得把之前的几个节点的slot重新分配一下就OK了,收缩也是这样的一个流程。总体来说就是一个rebalance的过程。
Client是如何操作Cluster的
我们在client中对集群操作的时候,它只要接收到任何与key相关的命令都会计算这个key所在的slot,然后再根据slot找出对应的节点。如果是当前节点就会处理这条命令,如果不是就会爆出MOVED错误,我们的Cluster就会很智能的提示你 这个key是在哪个槽,你应该去哪个节点来进行操作,而不是在这个节点中操作。这个操作就被称为重定向。
我们在打开cli的时候可以加入 -c 这个参数,加了这个参数就可以自动重定向了。
Sentinel与Cluster的比较
- CAP原则上不一样,Sentinel用的是CP原则而Cluster用的是AP原则
- Sentinel在目的上就是为了解决主从模式下的无法进行自动化故障恢复,而Cluster则是为了高并发来设计的
参考文献
《Redis开发与运维》 ——付磊 张益军