【架构师修炼之路】

本文目录


本文主要介绍 Redis 集群主节点故障的解决方案: 哨兵机制.

解决什么问题

Redis 集群中, master 主节点发生故障怎么办?


Redis主从拓扑
哨兵(Sentinel)主要是为了解决在主从复制架构中出现宕机的情况,主要分为两种情况:
1).从Redis宕机

这个相对而言比较简单,在Redis中从库重新启动后会自动加入到主从架构中,自动完成同步数据。在Redis2.8版本后,主从断线后恢复
的情况下实现增量复制。
2).主Redis宕机

这个相对而言就会复杂一些,需要以下2步才能完成
a. 在从数据库中执行SLAVEOF NO ONE命令,断开主从关系并且提升为主库继续服务
b. 第二步,将主库重新启动后,执行SLAVEOF命令,将其设置为其他库的从库,这时数据就能更新回来
由于这个手动完成恢复的过程其实是比较麻烦的并且容易出错,所以Redis提供的哨兵(sentinel)的功能来解决.

实现目标

实现 redis 故障转移的自动化。
自动发现,自动转移。
不需要人工参与。

架构拓扑


Redis Sentinel 是一个分布式系统,为Redis提供高可用性解决方案。可以在一个架构中运行多个 Sentinel 进程(progress), 这些进程使用流言协议 (gossip protocols) 来接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故 障迁移, 以及选择哪个从服务器作为新的主服务器。

核心思想

Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例 组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。
如图所示


在Server1 掉线后:


升级Server2 为新的主服务器:


Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance) 该系统执行以下三个任务:

  • 监控(Monitoring): Sentinel 会不断地定期检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automaticfailover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中 一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客 户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主 服务器代替失效服务器。

哨兵leader选举算法

如果主节点被判定为客观下线之后,就要选取一个哨兵节点来完成后面的故障转移工作,选举出一个leader的流程如下:

a)每个在线的哨兵节点都可以成为领导者,当它确认(比如哨兵3)主节点下线时,会向其它哨兵发 is-master-down-by-addr
命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;
b)当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者;

c)如果哨兵3发现自己在选举的票数大于等于 num(sentinels)/2+1
时,将成为领导者,如果没有超过,继续选举…………

主观下线:
所谓主观下线,就是单个sentinel认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)。
sentinel会以每秒一次的频率向所有与其建立了命令连接的实例(master,从服务,其他sentinel)发ping命令,通过判断ping回复是有效回复,还是无效回复来判断实例时候在线(对该sentinel来说是“主观在线”)。
sentinel配置文件中的down-after-milliseconds设置了判断主观下线的时间长度,如果实例在down-after-milliseconds毫秒内,返回的都是无效回复,那么sentinel回认为该实例已(主观)下线,修改其flags状态为SRI_S_DOWN。如果多个sentinel监视一个服务,有可能存在多个sentinel的down-after-milliseconds配置不同,这个在实际生产中要注意。

客观下线:
当主观下线的节点是主节点时,此时该哨兵3节点会通过指令sentinel is-masterdown-by-addr寻求其它哨兵节点对主节点的判断,如果其他的哨兵也认为主节点主观线下了,则当认为主观下线的票数超过了quorum(选举)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线:


哨兵至少需要3个实例,来保证自己的健壮性。哨兵+redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性. 对于哨兵+redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充分的测试和演练。

自动故障转移机制

在从节点(slave node) 中选择新的主节点(master node)

sentinel状态数据结构中保存了主服务的所有从服务信息,领头sentinel按照如下的规则从从服务列表中挑选出新的主服务

  1. 过滤掉主观下线的节点
  2. 选择slave-priority最高的节点,如果由则返回没有就继续选择
  3. 选择出复制偏移量最大的系节点,因为复制便宜量越大则数据复制的越完整,如果由就返回了,没有就继续
  4. 选择run_id最小的节点

更新主从状态

通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。
将已下线的主节点设置成新的主节点的从节点,当其回复正常时,复制新的主节点,变成新的主节点的从节点.

redis哨兵主备切换的数据丢失问题

两种丢失情况:

异步复制

因为master->slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,这些数据就丢失了。

脑裂

脑裂,也就是说,某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着, 这个时候,集群中就会出现两个master。
此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master数据可能就会丢失。因此master在恢复的时候,会被作为一个slave挂到新的master上,自己的数据会被清空,从新的master复制数据,

解决异步复制和脑裂导致的数据丢失

设置数据复制和同步的延迟时间:

min-slaves-to-write 1
min-slaves-max-lag 10

要求至少有1个slave,数据复制和同步的延迟不能超过10秒
如果说一旦所有slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了。

(1)减少异步复制的数据丢失
有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内
(2)减少脑裂的数据丢失
如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒没有给自己ack消息,那么就直接拒绝客户端的写请求.

这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失.
上面的配置就确保了,如果跟任何一个slave丢了连接,在10秒后发现没有slave给自己ack,那么就拒绝新的写请求.因此在脑裂场景下,最多就丢失10秒的数据

总结

哨兵架构,几乎可以做到了我们的要实现的高可用,但是哨兵的选举还是需要时间的,而且中间会阻塞客户端的请求,假如我们的选举消耗了1秒(实际可能几秒,高则几十秒),就在这1秒的时候来了客户端的请求,那个请求也是不可用的,并且我们的读写的节点实际还是单节点的,怎么办? 使用 Redis集群架构:


也就是我们Redis的集群其实就是一个个小的主从结合在一起(官方建议小于1000个小主从),变成了我们的Redis集群,每个小主从也就是我们的Redis数据分片。

Kotlin 开发者社区


国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。
越是喧嚣的世界,越需要宁静的思考。