业务高可用的保障:异地多活架构
异地就是指地理位置上不同的地方;多活就是指不同地理位置上的系统都能够提供业务服务
判断一个系统是否符合异地多活,需要满足两个标准:
- 正常情况下,用户无论访问哪一个地点的业务系统,都能够得到正确的业务服务。
- 某个地方业务异常的时候,用户访问其他地方正常的业务系统,能够得到正确的业务服务。
异地多活的成本较高,要在一个或者多个机房搭建独立的一套业务系统。系统复杂度也比较高。
场景:支付宝就需要异地多活。但是像新闻网站异地备份就可以了,因为即使业务服务中断,对用户的影响并不会很大
一、异地多活的种类
根据地理位置上距离来划分,异地多活架构可以分为同城异区、跨城异地、跨国异地
1. 同城异区
将业务部署在同一个城市不同区的多个机房。
同城的两个机房,距离上一般大约就是几十千米,通过搭建高速的网络,同城异区的两个机房能够实现和同一个机房内几乎一样的网络传输速度。这就意味着虽然是两个不同地理位置上的机房,但逻辑上我们可以将它们看作同一个机房,这样的设计大大降低了复杂度,减少了异地多活的设计和实现复杂度及成本。
但是如果遇到整个城市的地震就无能为力,但是解决机房火灾、机房停电、空掉故障之类没有问题
因此,结合复杂度、成本、故障发生概率来综合考虑,同城异区是应对机房级别故障的最优架构
2. 跨城异地
业务部署在不同城市的多个机房,而且距离最好要远一些。
距离增加带来的最主要问题是两个机房的网络传输速度会降低,这不是以人的意志为转移的,而是物理定律决定的,即光速真空传播大约是每秒 30 万千米,在光纤中传输的速度大约是每秒 20 万千米,再加上传输中的各种网络设备的处理,实际还远远达不到理论上的速度。
除了距离上的限制,中间传输各种不可控的因素也非常多。例如,挖掘机把光纤挖断、中美海底电缆被拖船扯断、骨干网故障等,这些线路很多是第三方维护,针对故障我们根本无能为力也无法预知。例如,广州机房到北京机房,正常情况下 RTT 大约是 50 毫秒左右,遇到网络波动之类的情况,RTT 可能飙升到 500 毫秒甚至 1 秒,更不用说经常发生的线路丢包问题,那延迟可能就是几秒几十秒了。
即根据数据的特性来做不同的架构。如果是强一致性要求的数据,例如银行存款余额、支付宝余额等,这类数据实际上是无法做到跨城异地多活的,而只能采用同城异区这种架构。比如系统分别部署在广州和北京,用户A 余额有 1W,广州和北京机房都有这个数据,A 向 B 转了 5K,这个操作是在广州完成,此时广州机房 A 的余额是 5K。广州和北京的网络电缆被挖断了,广州机房无法将余额通知给北京机房,此时北京机房还是 1W。A 到北京机房继续转帐。。。
场景:对于向用户登录(数据不一致重新登录即可)、新闻类网站(一天内的新闻数据变化较少)就适合异地多活。关键在于数据不一致的情况下,业务不受影响或者影响很小。A 城市机房挂了,B 城市机房依然可以提供服务,不过可能数据有不一致
3. 跨国异地
业务部署在不同国家的多个机房。因此数据同步的延时会更长,正常情况下可能就有几秒钟了。这种程度的延迟已经无法满足异地多活标准的第一条:“正常情况下,用户无论访问哪一个地点的业务系统,都能够得到正确的业务服务”
跨国异地多活场景
- 为不同地区用户提供服务
- 只读业务做多活
二、跨城异地多活的四大技巧
1. 保证核心业务的异地多活
2. 保证核心数据最终一致性
异地多活本质上是通过异地的数据冗余,来保证在极端异常的情况下业务也能够正常提供给用户,因此数据同步是异地多活架构设计的核心。完美主义:我要所有数据都实时同步。
异地多活架构面临一个无法解决的矛盾:业务上要求数据快速同步,物理上做不到数据快速同步。因此所有数据都实时同步,是一个无法达到的目标,参考方法:
- 尽量减少异地多活机房的距离,搭建告诉网络。成本巨大
- 尽量减少数据同步,只同步核心业务相关数据
- 保证最终一致性,不保证实时一致性
3. 采用多种手段同步数据
存储系统(es、redis、mysql)的同步方案可能有问题,或者不适合某些场景。Mysql 5.1 版本的复制是单线程的复制,网络抖动或者大量数据同步时延迟较长,且难以采取什么措施,只能干等。Redis 2.8 之前的版本,主从复制,从机断开重连,主机都会触发全量的主从复制,此时主机压力大,从机无法提供服务,数据量大,恢复时间长。
因此,我们采用多种手段配合存储系统的同步来同步数据,如下:
- 消息队列方式。对于账号数据,账号只会创建,不会修改,删除功能不提供,我们可以将账号数据通过消息队列同步到其他业务中心
- 二次读取方式。用户在中心 A 注册,然后访问中心 B 的业务,中心 B 先在本地拿,拿不到再从中心 A 访问一次
- 存储系统同步方式。对于密码数据,由于用户改密码的频率较低,而且用户不可能在 1 秒内连续改多次密码,因此使用数据库的同步机制即可
- 回源读取方式。对于登录的 session 数据,由于数据量大,可以不同步;用户A在中心A登录后,又在B中心登录,B中心拿到用户上传的 session id 后,根据路由判断 session 属于 A 中心,直接去A 中心请求 session 数据即可。
- 重新生成数据方式。对于“回源读取”场景,如果A中心宕机了,B中心请求 session 数据失败,此时就只能登录失败,让用户重新在中心B登录,生成新的 session 数据
4. 只保证绝大部分用户的异地多活
异地多活一个典型的思维误区:我要保证业务 100% 可用。异地多活无法保证 100% 的业务可用,这是由物理规律决定的,光速和网络的传播速度、硬盘的读写速度、极端异常情况的不可控等,都是无法 100% 解决的。因此,我们需要“忍”,忍受一小部分用户或者业务上的损失。
对于某些实时强一致性的业务,比如银行业务,技术上无法保证实时转账的异地多活,可以从业务的角度来实现,比如:转账业务除了“实时转账”外,还提供“转账申请”业务,即小明在上海业务中心提交转账请求,但上海的业务中心并不立即转账,而是记录这个转账请求,然后后台异步发起真正的转账操作,如果此时北京业务中心不可用,转账请求可以继续等待重试;假设等待 2小时后北京业务中心恢复了,此时上海业务中心去请求转账,发现余额不够,转账就失败。小明的申请就失败,原因是余额不足。类似微信中银行卡提现
不过需要注意,转账申请虽然有助于实现异地多活,但是牺牲了用户体验,我们可以有一些弥补措施。挂公告、事后补偿等
异地多活的设计理念总结:采用多种手段,保证绝大部分用户的核心业务异地多活
三、跨城异地多活设计
1. 第一步:业务分级
按照一定的标准将业务进行分级,挑选出核心的业务,只为核心业务设计异地多活,降低方案整体复杂度和实现成本。常见的分级标准有:
- 访问量大的业务
- 核心业务
- 产生大量收入的业务
2. 第二步:数据分类
挑选出核心业务后,需要对核心业务相关的数据进一步分析,目的在于识别所有的数据及数据特征,这些数据特征会影响后面的方案设计。常见的数据特征分析维度有:
- 数据量,包括总的数据量和新增、修改、删除的量,数据量越大,同步延迟的几率越高,同步方案需要考虑相应的解决方案
- 唯一性,数据是否要求多个异地机房产生的同类数据必须保证唯一。比如,用户ID,两个机房的不同用户注册后生成了一样的用户ID,业务会出错。数据的唯一性影响业务的多活设计,如果数据不需要唯一,那就说明两个地方都产生同类数据是可能的;如果数据要求必须唯一,要么只能一个中心点产生数据,要么需要设计一个数据唯一生成的算法
- 实时性,实时性指如果在 A 机房修改了数据,要求多长时间必须同步到 B 机房,实时性要求越高,对同步的要求越高,方案越复杂
- 可丢失性,数据是否可以丢失。例如,写入 A 机房的数据还没有同步到 B 机房,此时 A 机房机器宕机会导致数据丢失,那这部分丢失的数据是否对业务会产生重大影响。比如登录过程中产生的 session 数据就是可丢失的
- 可恢复性,数据丢失后,是否可以通过某种手段进行恢复。比如用户密码丢失,用户可以通过找回密码来重新设置一个新密码,这也算是可以恢复的;而用户账号如果丢失,用户无法登录系统,系统也无法通过其他途径来恢复这个账号,这就是不可恢复的数据
3. 第三步:数据同步
常见的数据同步方案有:
- 存储系统同步,比如 MySQL 数据主从同步。使用简单,缺点是这类同步方案都是通用的,无法针对业务数据特点做定制化的控制。例如:无论需要同步的数据量有多大,MySQL 都只有一个同步通道,因为要保证事务性,一旦数据量较大,或者网络延迟,则同步延迟就会比较严重
- 消息队列同步,比如 Kafka、ActiveMQ 等。消息队列同步适合无事务性或者无时序性要求的数据。比如对于新注册的用户账号,我们可以采用消息队列同步了;而对于用户密码,就不能采用消息队列同步了。
- 重复生产,数据不同步到异地机房,每个机房都可以生成数据,这个方案适合于可以重复生成的数据。例如,登录产生的 cookie、session 数据、缓存数据等
4. 第四步:异常处理
一旦极端情况出现,总会有部分数据出现异常。假设在出现这些问题时,系统将采取什么措施来应对。异常处理主要有以下几个目的:
- 问题发生时,避免少量数据异常导致整体业务不可用。
- 问题恢复后,将异常的数据进行修正。
- 对用户进行安抚,弥补用户损失。
常见的异常处理措施
多通道同步
采取多种方式来进行数据同步,其中某条通道故障的情况下,系统可以通过其他方式来进行同步,这种方式可以应对同步通道处故障的情况
设计关键:
- 一般情况下,采取两通道即可,采取更多通道理论上能够降低风险,但付出的成本也会增加很多。
- 数据库同步通道和消息队列同步通道不能采用相同的网络连接,否则一旦网络故障,两个通道都同时故障;可以一个走公网连接,一个走内网连接。
- 需要数据是可以重复覆盖的,即无论哪个通道先到哪个通道后到,最终结果是一样的。例如,新建账号数据就符合这个标准,而密码数据则不符合这个标准。
同步和访问结合
访问指异地机房通过系统的接口来进行数据访问。例如业务部署在异地两个机房 A 和 B,B 机房的业务系统通过接口来访问 A 机房的系统获取账号信息。设计关键点:
- 接口访问通道和数据库同步通道不能采用相同的网络连接,不能让数据库同步和接口访问都走同一条网络通道,可以采用接口访问走公网连接,数据库同步走内网连接这种方式
- 数据有路由规则,可以根据数据来推断应该访问哪个机房的接口来读取数据。例如,有 3 个机房 A、B、C,B 机房拿到一个不属于 B 机房的数据后,需要根据路由规则判断是访问 A 机房接口,还是访问 C 机房接口
- 由于有同步通道,优先读取本地数据,本地数据无法读取到再通过接口去访问,这样可以大大降低跨机房的异地接口访问数量,适合于实时性要求非常高的数据
日志记录
主要用于用户故障恢复后对数据进行恢复,其主要方式是每个关键操作前后都记录相关一条日志,然后将日志保存在一个独立的地方,当故障恢复后,拿出日志跟数据进行对比,对数据进行修复为了应对不同级别的故障,日志保存的要求也不一样,常见的日志保存方式有:
- 服务器上保存日志,数据库中保存数据,这种方式可以应对单台数据库服务器故障或者宕机的情况。
- 本地独立系统保存日志,这种方式可以应对某业务服务器和数据库同时宕机的情况。例如,服务器和数据库部署在同一个机架,或者同一个电源线路上,就会出现服务器和数据库同时宕机的情况。
- 日志异地保存,这种方式可以应对机房宕机的情况。
用户补偿