21

【架构师修炼之路】

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzA5OTI2MTE3NA%3D%3D&%3Bmid=2658338085&%3Bidx=1&%3Bsn=6be796d9c1a3106a63be0f66b191ebbb
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文目录

uMF7Zn7.jpg!web

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

解决什么问题

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

ziiIn2e.jpg!web

Redis主从拓扑

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

1).从Redis宕机

这个相对而言比较简单,在Redis中从库重新启动后会自动加入到主从架构中,自动完成同步数据。在Redis2.8版本后,主从断线后恢复

的情况下实现增量复制。

2).主Redis宕机

这个相对而言就会复杂一些,需要以下2步才能完成

a. 在从数据库中执行SLAVEOF NO ONE命令,断开主从关系并且提升为主库继续服务

b. 第二步,将主库重新启动后,执行SLAVEOF命令,将其设置为其他库的从库,这时数据就能更新回来

由于这个手动完成恢复的过程其实是比较麻烦的并且容易出错,所以Redis提供的哨兵(sentinel)的功能来解决.

实现目标

实现 redis 故障转移的自动化。

自动发现,自动转移。

不需要人工参与。

架构拓扑

eaInmqY.jpg!web

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

UbAvI3V.jpg!web

核心思想

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

如图所示

Vr6FJzQ.jpg!web

在Server1 掉线后:

iyAbmeu.jpg!web

升级Server2 为新的主服务器:

ZFNNZbz.jpg!web

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 时,将成为领导者,如果没有超过,继续选举…………

R3yMRfQ.jpg!web

主观下线: 所谓主观下线,就是单个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(选举)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线:

BV3yIrY.jpg!web

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

自动故障转移机制

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

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

  1. 过滤掉主观下线的节点

  2. 选择slave-priority最高的节点,如果由则返回没有就继续选择

  3. 选择出复制偏移量最大的系节点,因为复制便宜量越大则数据复制的越完整,如果由就返回了,没有就继续

  4. 选择run_id最小的节点

z6buema.jpg!web

更新主从状态

通过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集群架构:

v2memmU.jpg!web

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

Kotlin 开发者社区

JvYni2n.jpg!web

国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

越是喧嚣的世界,越需要宁静的思考。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK