异地多活

异地多活

异地多活,英文 Multi-Site High Availability,顾名思义就是分布在异地多个站点同时对外提供服务。与传统的灾备的最主要区别就是“多活”里的所有站点都是同时在对外提供服务的。

业务场景

读多写少型业务

典型的业务场景就是资讯、导购类的服务,比如商品浏览、新闻资讯等。典型的数据特点就是读多写少,用户以浏览为主,核心业务场景只读,单元里部署的都是只读业务。接入成本低,只需要用户在请求里可以标记上分流的标识即可

流水单据型业务

典型的业务场景就是电商交易、帐单流水类服务,比如用户的订单、用户的通话记录等。典型的数据特点就是数据可以按照一定的维度进行分片且可以接受最终一致。接入成本略高,用户需要梳理业务,理出单元内的部署的核心的业务及其数据,对于单元依赖且无法拆分的业务采用读写分离,然后按照多活接入规范重点对服务层及数据层进行相应改造即可。

状态依赖型业务

典型的业务场景就是银行账务,比如 A、B 在某银行均有帐户,A、B 数据分片位于不同的数据中心,A 和 B 之间会有转账行为。典型的数据特点就是数据有状态依赖且无法最终一致,数据还存在跨数据中心的交互。接入成本高。

设计原则

避免过度设计

对于一些实时性要求极高,无法接受最终一致的数据只能进行单点写。

  • 实时异地多活

异地多活本质上是通过异地的数据冗余,来保证在极端异常的情况下业务也能够正常提供给用户,因此数据同步是异地多活设计方案的核心。但由于目前的技术还无法突破数据传输与物理距离解耦合,也就是说两个异地数据中心之间一定存在一定程度的延时,如果业务对实时性要求极高,那就无法实现异地多活。

  • 所有用户异地多活

同样是由于物理距离造成的数据传输的延迟,数据只能做到最终一致,在一些极端情况下,部分用户的数据无法及时同步到新切换到的中心,这时,这部分用户的业务会受到一定程度的影响。

  • 所有业务异地多活

异地多活效果看起来很诱人,但如果不假思索贪大求全的要求所有业务都实现异地多活的话,很可能什么也做不成。原因是异地多活是有成本的,会根据业务类型有不太一样的开发和运维成本,但如果全做加之对业务梳理不到位的话,会造成整个工期不可控,即使完成,上线后的效果也需求多重验证。

  • 通用的异地多活

异地多活的实现根据业务类似的不同也有一定程度的差异,即使为了让业务尽量少的做改动,尽可能把这种能力都封装在中间件和基础工具中,但即便如此,也无法保证这些能覆盖所有的业务类似。遇到特殊的业务类型或者用户自己实现的基础组件,仍然需要根据具体的业务特点也制定新的多活方案。

业务合理切片

选择一个数据维度来做数据切片,进而实现业务可以分开部署在不同的数据中心,选择与上次选取的数据维度相关的业务范围来做多活。

单元封闭

尽量让调用发生在本单元,尽量避免跨数据中心的调用,一方面为了用户体验,本地调用 RT 更短,另一方面为了稳定性,防止一个数据中心出了问题,其它数据中心受影响。

冷备份

机房之间的延时:微博北京的两个核心机房间延时在 1ms 左右,但北京机房到广州机房则有近 40ms 的延时。对比一下,微博核心 Feed 接口的总平均耗时也就在 120ms 左右。微博 Feed 会依赖几十个服务上百个资源,如果都跨机房请求,性能将会惨不忍睹;专线问题:为了做广州机房外部,微博租了两条北京到广州的专线,成本巨大。同时单条专线的稳定性也很难保障,基本上每个月都会有或大或小的问题;数据同步问题:MySQL 如何做数据同步?HBase 如何做数据同步?还有各种自研的组件,这些统统要做多机房数据同步。几十毫秒的延时,加上路途遥远导致的较弱网络质量(我们的专线每个月都会有或大或小的问题),数据同步是非常大的挑战;依赖服务部署问题:如同阿里目前只做了交易单元的 “ 异地双活 ”,微博部署时也面临核心服务过多依赖小服务的问题。将小服务全部部署改造成本、维护成本过大,不部署则会遇到之前提到的机房之间延时导致整体性能无法接受的问题;配套体系问题:只是服务部署没有流量引入就不能称为 “ 双活 ”,而要引入流量就要求配套的服务和流程都能支持异地部署,包括预览、发布、测试、监控、降级等都要进行相应改造;

整个业界传统的做法,异地是用来做一个冷备份的,等这边另外一个城市全部挂掉了,才会切过去。我们当时也是按照这个方式去尝试的,尝试了一年左右,我们觉 得冷备的方向对我们来讲有两个问题:第一个问题是成本太高。我们需要备份全站,而整个阿里巴巴网站,包括淘宝、天猫、聚划算等等,所有加起来,是一个非常 大的量,备份成本非常高。第二个问题是,冷备并不是一直在跑流量的,所以有个问题,一旦真的出问题了,未必敢切过去。因为不知道切过去到底能不能起来,而 且整个冷备恢复起来要花多长时间,也不敢保证。因此在尝试之后,我们发现这个方向对我们而言并不好。最关键的原因是,我们在 2013 年左右碰到了几个问题。首先,阿里巴巴的机房不仅仅给电商用,我们有电商,有物流,有云,有大数据,所有业务共用机 房。随着各种业务规模的不断增长,单个城市可能很难容纳下我们,所以我们面临的问题是,一定需要去不同的城市建设我们的数据中心。其次是我们的伸缩规模的 问题。整个淘宝的量,交易量不断攀升,每年的双十一大家都看到了,增长非常快。而我们的架构更多还是在 2009 年以前确定的一套架构,要不断的加机器,这 套架构会面临风险。如果能够做到异地部署,就可以把伸缩规模缩小。虽然原来就是一套巨大的分布式应用,但是其实可以认为是一个集群,然后不断的加机器。但是在这种情况下,随着不断加机器,最终在整个分布式体系中,一定会有一个点是会出现风险的,会再次到达瓶颈,那时就无法解决了。

异地部署

异地部署,从整个业界的做法上来讲,主要有几家公司,比如 Google、Facebook,这两家是比较典型的,Google 做到了全球多个数据中 心,都是多活的。但是 Google 具体怎么做的,也没有多少人了解。另外一家就是 Facebook,我们相对更了解一些,Facebook 在做多个数据中 心时,比如说美国和欧洲两个数据中心,确实都在支撑流量。但是欧洲的用户有可能需要访问美国的数据中心,当出现这种状况时,整个用户体验不是很好。国内的情况,我们知道的像银行,还有其他一些行业,倾向于做异地灾备。银行一般都会有两地,一个地方是热点,另一个地方是冷备。当遇到故障时,就没 有办法做出决定,到底要不要切到灾备数据中心,他们会碰到我们以前摸索时所面对的问题,就是不确定切换过程到底要多久,灾备中心到底多久才能把流量接管起 来。而且接管以后,整个功能是不是都正常,也可能无法确定。刚才也提到过,冷备的话,我们要备份全站,成本是非常高的。如果每个地点都是活的,这些数据中心就可以实时承担流量,任何一点出问题,都可以直接切掉,由另外一点直接接管。相对冷备而言,这是一套可以运行的模式,而且风险非常低。

在解决挑战的过程中,我们会面临更细节的一些问题。怎样降低延时的影响,我们能想到的最简单、最好的办法,就是让操作全部在同一机房内完成,那就不存在延时的挑战了。所以最关键的问题,就是怎样让所有操作在一个机房内完成。这就是单元化。为什么叫单元化,而没有叫其他名字呢?原因在于,要在异地部署我们的网站,首先要做一个决定。比如说,冷备是把整个站全部备过去,这样可以确保可以全部接管。但是多活的情况下,要考虑成本,所以不能部署全站。

这时淘宝会面临一个比 Google、Facebook 等公司更大的一个挑战。像 Facebook 目前做的全球化数据中心,因为 Facebook 更多的是用户和用户之间发消息,属于社交领域。但淘宝是电商领域,对数据的一致性要求非常高,延时要求也非常高。还有个更大的挑战,那就是淘宝的数据。如果要把用户操作封闭在一个单元内完成,最关键的是数据。跟冷备相比,异地多活最大的风险在于,它的数据会同 时在多个地方写,冷备则不存在数据会写错的问题。如果多个地方在写同一行数据,那就没有办法判断哪条数据是正确的。在某个点,必须确保单行的数据在一个地 方写,绝对不能在多个地方写。为了做到这一点,必须确定数据的维度。如果数据只有一个维度,就像 Facebook 的数据,可以认为只有一个纬度,就是用户。但是像淘宝,如果要在 淘宝上买一个东西,除了用户本身的信息以外,还会看到所有商品的数据、所有卖家的数据,面对的是买家、卖家和商品三个维度。这时就必须做出一个选择,到底 用哪个维度作为我们唯一的一个封闭的维度。单元化时,走向异地的就是买家的核心链路,所以我们选择了买家这个维度。但是这样自然会带来一个问题,因为我们有三个维度的数据,当操作卖家商品数据时,就无法封闭了,因为这时一定会出现需要集中到一个点去写的现象。从我们的角度看,目前实现整个单元化项目最大的几个难点是:第一个是路由的一致性。因为我们是按买家维度来切分数据的,就是数据会封闭在不同的单元里。这时就要确保,这个买家相关的数据在写的时候,一定是要 写在那个单元里,而不能在另外一个单元,否则就会出现同一行数据在两个地方写的现象。所以这时一定要保证,一个用户进到淘宝,要通过一个路由规则来决定这 个用户去哪里。这个用户进来以后,到了前端的一组 Web 页面,而 Web 页面背后还要访问很多后端服务,服务又要访问数据库,所以最关键的是要保障这个用户 从进来一直到访问服务,到访问数据库,全链路的路由规则都是完全一致的。如果说某个用户本来应该进 A 城市的数据中心,但是却因为路由错误,进入了 B 城市,那看到的数据就是错的了。造成的结果,可能是用户看到的购买列表是空的,这是不能接受的。所以如何保障路由的一致性,非常关键。第二个是挑战是数据的延时问题。因为是异地部署,我们需要同步卖家的数据、商品的数据。如果同步的延时太长,就会影响用户体验。我们能接受的范围是 有限的,如果是 10 秒、30 秒,用户就会感知到。比如说卖家改了一个价格,改了一个库存,而用户隔了很久才看到,这对买家和卖家都是伤害。所以我们能接受 的延时必须要做到一秒内,即在全国的范围内,都必须做到一秒内把数据同步完。当时所有的开源方案,或者公开的方案,包括 MySQL 自己的主备等,其实都不 可能做到一秒内,所以数据延时是我们当时面临的第二个挑战。第三个挑战,在所有的异地项目中,虽然冷备成本很高,多活可以降低成本,但是为什么大家更喜欢冷备,而不喜欢多活呢?因为数据的正确性很难保证。数 据在多点同时写的时候,一定不能写错。因为数据故障跟业务故障还不一样,跟应用层故障不一样。如果应用出故障了,可能就是用户不能访问。但是如果数据写错 了,对用户来说,就彻底乱了。而且这个故障是无法恢复的,因为无法确定到底那里写的才是对的。所以在所有的异地多活项目中,最重要的是保障某个点写进去的 数据一定是正确的。这是最大的挑战,也是我们在设计整个方案中的第一原则。业务这一层出故障我们都可以接受,但是不能接受数据故障。还有一个挑战是数据的一致性。多个单元之间一定会有数据同步。一方面,每个单元都需要卖家的数据、商品的数据;另一方面,我们的单元不是全量业务,那一定会有业务需要这个单元,比如说买家在这个单元下了一笔定单,而其他业务有可能也是需要这笔数据,否则可能操作不了,所以需要同步该数据。所以怎样确 保每个单元之间的商品、卖家的数据是一致的,然后买家数据中心和单元是一致的,这是非常关键的。这几个挑战可能是整个异地多活项目中最复杂的。另外还有一点,淘宝目前还是一个高速发展的业务,在这样的过程中,去做一次比较纯技术的改造,怎样确保对业务的影响最小,也是一个挑战。

上一页