Redis集群
主从模式
主服务器实时同步数据到从服务器中。防止主服务器出现单点事故,导致数据丢失。
参考Redis主从复制原理
Sentinel模式(哨兵模式)
Sentinel是Redis高可用的一个解决方法,一个或多个Sentinel实例负责监控多个主服务器和这些主服务器的从服务器。在被监视的主服务器处于下线状态时,将下线的主服务器的某个从服务器升级为新的主服务器,然后由新的主服务器处理命令请求。
选举领头Sentinel
如果一个主服务器被检测到下线,监视这个主服务器的多个Sentinel会进行协商选举出一个领头Sentinel,然后由领头Sentinel对下线的主服务器进行故障转移。选举领头Sentinel的方法是对Raft算法的实现。
Redis选举领头Sentinel规则:
- 所有在线Sentinel机会均等。所有在线的Sentinel都有资格被选举为领头Sentinel。
- Sentinel配置纪元。每次选举无论是否产生新的领头Sentinel,纪元都会增加一次。
- 在一个纪元中,每一个Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会。一旦确定局部领头Sentinel,在这个纪元中就不可以再更改。
下面是选举过程:
- 发现主服务器下线的每个Sentinel都会发送消息要求其他的Sentinel设置自己为局部领头Sentinel。
- Sentinel设置局部领头Sentinel,采用消息先到先得的原则。例如Sentinel A和Sentinel B同时向Sentinel C发送消息请求成为局部领头Sentinel,Sentinel C先收到Sentinel A的消息,所以设置Sentinel A为自己的局部领头Sentinel。后到的Sentinel B消息会被Sentinel C拒绝。
- 收到消息后,设置局部领头Sentinel的Sentinel会发送确认消息。如上,Sentinel C会向Sentinel A发送确认消息,确认Sentinel A已经是自己的局部领头Sentinel。
- 成为局部领头的Sentinel收到确认消息后,会对照信息确认自己成为局部领头的Sentinel。Sentinel A收到Sentinel C发来的回复消息,对比自己的运行ID与消息中的运行ID一致,确认自己成为了Sentinel C的局本领头Sentinel。
选举结果产生:
- 如果某个Sentinel被半数以上的Sentinel设置为局部领头Sentinel,则这个Sentinel会成为领头Sentinel。例如10个Sentinel系统中,有5个以上的Sentinel将某个Sentinel设置为局部领头Sentinel,则成功选举出领头Sentinel。
- 因为领头Sentinel的产生需要半数以上的Sentinel支持,并且在一个纪元中,每个Sentinel只能设置一次局部领头Sentinel,所以在一个纪元中只会产生一个领头Sentinel。
选举失败:
- 如果在给定时限没有产生领头Sentinel,那么所有的Sentinel将在一段时间之后再次举行选举,直到产生领头Sentinel为止。
故障转移——选举出新的主服务器
在选举出领头Sentinel后,领头Sentinel对下线的主服务器进行故障转移。
- 在已下线的主服务器的从服务器中选出一个作为新的主服务器。
- 所有其他已下线主服务器的从服务器复制新的主服务器。
- 已下线主服务器设置为新的主服务器的从服务器。当其再次上线时,作为新的主服务器的从服务器运行。
选出新的主服务器
- 剔除处于下线或断线的从服务器。
- 剔除最近5秒内没有回复领头Sentinel INFO命令的从服务器。
- 剔除与已下线主服务器断开超时的从服务器。保证从服务器保存的数据尽可能新。
- 根据优先级排序,选择优先级最高的从服务器作为新的主服务器。
- 优先级相同,选择偏移量最大的从服务器,这样的从服务器数据最新。
- 最大偏移量相同,按照运行ID排序,选择运行ID最小的从服务器作为新的主服务器。
集群模式
节点和槽
一个Redis集群由多个节点(node)组成。每个节点保存Redis的分片。Redis集群通过分片方式保存数据库中的键值对,集群的整个数据库被分为16384个槽(slot)。数据库中每个键值对都属于16384个槽之一。
集群中命令执行
- 接受命令的节点收到命令后,会计算出命令要处理的数据库键属于哪个槽。
计算属于哪个槽的函数:
计算属于哪个槽的函数
CRC16()计算key的CRC-16校验和,而&16383相当于对槽数16384取模,计算出一个介于0到16383之间的槽号。
- 如果键指派的槽正好属于自己,那么节点直接执行命令。
- 如果键指派的槽不属于自己,那么节点向客户端返回一个MOVED错误(其实就是一个重定向)。指引客户端指向正确的节点,再次发送之前想要执行的命令给正确的节点。
重新分片和ASK错误
重新分片
Redis集群的重新分片将任意数量已经指派给某个结点的槽,重新指派给另一个节点,并且相关槽所属的键值对也会同时移动到新节点上。重新分片操作执行时,集群仍旧可以处理命令请求,并且移动涉及的两个节点也都可以继续处理命令请求。
ASK错误
在进行重分片期间,正在移动数据到目标节点上的源节点,已经完成一部分数据的转移,此时客户端向源节点发送命令请求,并且命令要处理的键值对刚好属于被迁移的槽:
- 首先源节点会先在自己的节点上查找指定的键,如果数据尚未被迁移,就直接处理命令请求,并且返回结果。
- 如果数据已经被移动到目标节点上,在本地没有找到,源节点会返回一个ASK错误,ASK错误类似一个重定向,引导客户端指向目标节点,客户端向目标节点再次发送之前要执行的命令请求。
ASK错误只是两个节点在迁移槽时使用的一种临时措施。而MOVED错误代表槽的负责权已经从一个节点转移到另一个节点上。
故障转移
当主节点处于下线状态时:
- 从已下线的主节点的所有从节点中选择一个将要成为新的主节点。
- 被选中的从节点会执行SLAVE no one命令,成为新的主节点。
- 新的主节点撤销所有对已下线的主节点的槽指派,把这些槽改为指派给自己。
- 向集群发送广播,通知所有节点自己成为了主节点,负责管理已下线主节点之前负责的所有槽。
- 新的主节点正式上任,开始处理自己负责的槽相关的命令请求。
如何在从节点中选举新的主节点
与选举领头Sentinel算法相似,都是基于Raft算法。
- 集群中配置纪元,初始值为0。集群中某个结点开始故障转移,纪元自增一。
- 在一个纪元里,每个负责处理槽的主节点都有一次投票的机会,而第一个向主节点要求投票的从节点将获得主节点的投票。
开始选举
- 当从服务器发现自己复制的主服务器处于下线状态,会向集群广播通知自己的主节点已经下线,并且要求收到消息的主节点投票给自己。
- 具有投票权的主节点收到信息后,并且尚未给其他节点投票,会投票给要求投票的从节点,支持它成为主节点。
- 一个从节点获得主节点总数一半以上的投票,会当选为新的主节点。
- 在一个纪元中每个主节点只能投票给一个从节点,那么得票超过半数的从节点只能有一个,这确保新的主节点只能有一个。
选举失败,开始新纪元再次选举
- 如果在一个纪元中没有一个从节点得票过半,那么进入下一个纪元再次选举,直到选举出新的主节点为止。