基于阿里云MySQL/Redis/ElasticSearch的双活灾备方案的设计(文末附阿里云老用户福利)

avatar 2019年8月27日17:42:10 评论 89

说起容灾备份方案,一般说来有下面这个发展方向:

 

下面简单介绍下各个方案的内容:

冷备:离线手工对数据进行容灾备份,当发生故障时,手工切换到备用环境
热备:实时对主生产环境的数据进行备份,当发生故障时,自动或手工切换到灾备环境
双活:两套环境实时进行双向数据同步,每套环境都承载其中一部分流量,当发生故障时,只由其中一套环境承载所有流量
只有进行数据同步所涉及到的内部,一般包含如下内容:

数据库,一般是MySQL
Redis
MongoDB
搜索引擎,一般是ElasticSearch
大数据应用,一般是HBase和HDFS
由于容灾方案很多,下面只针对阿里云下的方案进行讲解。数据库方面,则只将MySQL,其他数据库可以触类旁通。搜索引擎则只讲ES(ElasticSearch)。

 

一、MySQL的数据同步

下面讲两种方案:

阿里云自带双活方案(DTS)
采用Canal实现
阿里的MySQL灾备方案,后面采用的就是阿里的DTS服务,可以根据流量等情况,选择服务的类型。DTS使用很方便,可以实现表级(只同步需要的表)甚至是字段级别的数据同步。RDS有重连机制,基本上可以保障在发送MySQL主从切换等情况下,数据不丢失,但是下面一些情况需要重点关注:

DTS无法规避MySQL写冲突,需要在业务上进行规避。最典型的场景,就是按照业务模块,或者按照客户归属性进行划分,保证两个中心,不会对同一条数据进行更新操作。
DTS有一定的表结构变化同步能力,但并不是全部,所以当发生表结构变化的时候,需要特别关注。第一种情况就是月表,如果是每个月生成一张月表,那么灾备中心可能就有问题,可以一次性生成未来好几年的月表。另外一种情况就是升级的时候,需要对表增加一个字段,这个时候最好是等待两个中心同时更新完成后,才进行数据同步。
延迟不可避免,最好不要对一个中心写,然后在另外一个中心读。
 

第二种方案就是采用开源框架canal,自己写代码实现MySQL的数据同步,它的处理流程如下:​​​​

canal的基本原理跟DTS一样,就是读取MySQL的binlog文件,然后将数据写入kafka中,另外一份订阅读取数据,并将数据写入到灾备数据库。

 

canal方案的好处就是开源的,不需要收费,当时它的可靠性肯定没有DTS高,下面地方需要特别注意:

主从切换下的数据丢失:正常情况下,canal进行数据同步,肯定不会发生数据丢失,但在MySQL异常,主从切换等各种异常情况下,如果处理不当,有可能发生少量数据丢失,所以需要评估业务是否能接受,如果要求极高的可靠性,最好采用阿里的方案。
双活方案:canal是单向同步的,而双活要求双向同步,那么采用canal的时候,就需要对数据变化进行时候,标识好那些是同步的数据,那些的变化的数据,要不然就容易发生同步的回路,导致死循环。
二、Redis的数据同步

阿里对Redis的内核做了改造,支持进行数据的同步,当时当前只支持全部的同步,所以当双活环境对同一个key进行操作的时候,则有可能发生问题,需要业务上需要对这个进行改造。

 

三、ES的数据同步

阿里当前没有支持ES的数据同步,所以只能自己实现,一般情况下,ES的写入有两种方式:

直接在代码里面实现,将数据写入ES
先将数据写入MySQL,然后再将MySQL的数据同步到ES
针对第一种方式,有一种可行的实现方式,就是将数据写入ES之后,将数据也写入一份到Kafka,然后灾备中心通过订阅Kafka,将数据写入到灾备的ES。

这里还有个双活,因为灾备中心的服务采用的是同一套代码,那么灾备中心往主中心同步的逻辑也是一样。

但这里有个问题,由于是同一套代码,所有最后是往Kafka的同一个Topic写数据,那么如何区分呢?下面有几个解决思路:

业务逻辑进行处理,不同的中心自动写入到不同的Topic
Topic通过配置实现,不同的中心配置不同的Topic
写入的数据增加标识,可以识别对应的中心。Kafka消费者在接收到消息后,丢弃不需要处理的数据
上面三种方案,推荐使用第一种。

 

针对ES写入的第二种方式,目前通常有两种实现方式:

通过Flume、Sqoop、Spark等工具,将数据从MySQL读取出来,然后写入到ES
通过canal,解析MySQL的binlog文件,然后写入数据到kafka,然后再写入到ES
第一种方案更加偏重于定时的数据同步,很多公司都是在凌晨执行进行数据的同步,由于该方案会对MySQL造成压力,如果MySQL做了kill的配置,很可能由于执行时间过长会被MySQL kill掉,造成数据丢失。

第二种方案是实时同步的方案,处理不当,在发生异常的情况下,有可能会出现少量的数据丢失。

如果只是需要凌晨同步,推荐用第一种方案,如果是对实时性要求比较高,推荐用第二种方案。

 

由于这两张方案通常都是写入到kafka,那么我们可以通过订阅kafka,实现同时往灾备中心写数据。

 

由于ES的集群方式,理论上还有另外一种方式,那就是一个ES集群,同时配置主中心和灾备中心的节点,当发生灾备时,理论上还有一半的节点可以使用。这个方案存在一个重大的缺陷,那就是灾备中心往往跟主中心距离遥远,比如说,主中心的南方,备中心在备份,如果一个ES集群同时包含不同地域的节点,那么这个时延可能无法接受,由于ES需要在不同的节点和分片上面进行数据的传输和备份,少量时延都可能对ES的性能造成大的影响,这对业务的感知就很差,所以不推荐该方式。

 

四、全局ID(自增ID)的设计

业务上面需要用到各种各样的唯一主键,在以前只有一个MySQL数据库的时代,全局ID往往是采用MySQL的自增ID来实现的,但随着规模的扩大,MySQL出现分表分库,并发量往往也在加大,MySQL的自增ID往往无法支撑性能,比较通常的的采用Redis来实现全局ID功能。

 

在双活或者灾备环境下,Redis全局ID和MySQL自增ID都存在问题,因为主环境和灾备环境是隔离的,双方产生的ID的可能存在重复。

 

解决的思路就是采用主环境和灾备环境分别生成不一样的ID,比如说,主环境产生单数的ID,灾备环境产生双数的ID,这样就保证两边产生的ID不重复了。

 

但这样就有另外一个问题,那就是有可能一个ID比较少的数据,产生的时间点却是比较晚的,在这种情况下,在灾备切换前,可以把灾备环境的ID刷新到最新的ID值,这样产生的数据就基本上是顺序的了。

 

五、定时任务的处理

正常的业务,后台都有各种定时任务,定时任务的类型主要分为两种:

统计类:每天凌晨对数据进行统计
迁移类:比如说每天凌晨,将数据迁移到历史表
监控类:比如说看门狗,用来监控系统是否正常
 

针对1和2两种类型,其实都可以认为是对MySQL的数据进行操作,而由于灾备会进行数据同步,如果备份中心也进行MySQL的数据处理,那么就会导致数据被重复处理,或者产生重复数据,所以备份中心的统计类和监控类定时任务不需要开启,只在主中心启动就行。

针对监控类定时任务,则灾备中心同时也需要启动。

 

六、公共数据的处理

假设双活的环境,业务不是完全隔离的话,那么就涉及到公共数据了,包括客户、权限、部门、系统参数等。

正常情况下,跟客户和客户所属地域相关的配置,是不会出现不同中心修改同一条记录的情况,那么也就不会出现写冲突了。

但是少量公共配置,比如说系统参数,还是可能出现写冲突。

 

比较稳妥的方式还是对这部分公共配置增加中心的配置,也就是不同有不同的配置,这样就不会出现写冲突了。

 

七、消息队列的处理

在微服务架构体系里,往往使用了RabbitMQ、Kafka等消息队列,通常的做法就是微服务在接受到请求并处理后,将下一步处理作为消息发送到消息队列,消息队列的消费者接收到消息后进行后续处理。

正常情况下,在双活系统里,主中心和灾备中心的消息队列分别处理各自地域的数据,互不干扰。

但是当消息队列处理的是公共数据的变化的,就可能有些问题了。比如说,消息队列是监听DB的变化,然后进行后续处理,那么灾备中心的服务应该停了,只由一个处理就可以。

 

八、告警监控的处理

正常情况下,告警监控主要包括以下内容:

主机告警监控
应用告警监控
日志分析和告警
业务告警
日常巡检
中间件告警
其中,应用告警主要是指java、php、go、c等服务,通常会监控应用对应的CPU、IO、缓存,吞吐量、慢事务、GC时间、缓存等。日志分析和告警主要是收集业务采集的日志,然后进行分析,通常使用ELK、GrayLog等,监控主要有异常日志,业务操作日志、请求日志等。业务告警主要是指业务专门写的测试代码,主要有业务拨测,自动化单元测试,自动化集成测试等。日常巡检通常就是运维人员每天,每周,每月执行的巡检操作,一般包括自动和手工。中间件告警包括MySQL、RabbitMQ、Kafka、Redis、MongoDB等。

双活情况下,上面告警监控都要能正常处理。

已有阿里云账号的老用户优惠

本公司是阿里云顶级代理商,通过项目报备的方式与我们关联后,无须改变你的购买方式(还是从阿里云官网下单付款购买),先买后返(最高返实际支付金额的15%)。如有采购需求,请及时和我们联系咨询。

+ QQ/微信:2040593

备注:阿里云老用户采购

avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: