「异地多活」是分布式系统架构设计的一座高峰 - 断点

一 入门 历史沿革

干货|搞懂「异地多活」,看这篇就够了 (baidu.com)

异地多活,作为一种高可用部署架构,成为大中型互联网公司的选择。像大家熟知的大型互联网公司,如阿里、腾讯、百度、网易、新浪等等都已经完成了异地多活的技术重构。

可以说,异地多活是互联网公司业务规模扩大后所必然要经历的阶段。那么如何解决高可用异地多活呢?

有状态服务

后台服务可以划分为两类,有状态和无状态。高可用对于无状态的应用来说是比较简单的,无状态的应用,只需要通过 F5 或者任何代理的方式就可以很好的解决。

后文描述的主要是针对有状态的服务进行分析。服务端进行状态维护主要是通过磁盘或内存进行保存,比如 MySQL 数据库,Redis 等内存数据库。

除了这两种类型的维护方式,还有 JVM 的内存的状态维持,但 JVM 的状态生命周期通常很短。

高可用,从发展来看,大致经过了这几个过程:

  • 冷备

  • 双机热备

  • 同城双活

  • 异地双活

  • 异地多活

异地多活淘宝架构文章:

(532条消息) 淘宝的高可用异地多活架构到底有多牛?_石杉的架构笔记的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_42046105/article/details/115683349

二 理论 cap-》BASE 最终一致性理论

好了,总结一下这篇文章的重点。

1、一个好的软件架构,应该遵循高性能、高可用、易扩展 3 大原则,其中「高可用」在系统规模变得越来越大时,变得尤为重要

2、系统发生故障并不可怕,能以「最快」的速度恢复,才是高可用追求的目标,异地多活是实现高可用的有效手段

3、提升高可用的核心是「冗余」,备份、主从副本、同城灾备、同城双活、两地三中心、异地双活,异地多活都是在做冗余

4、同城灾备分为「冷备」和「热备」,冷备只备份数据,不提供服务,热备实时同步数据,并做好随时切换的准备

5、同城双活比灾备的优势在于,两个机房都可以接入「读写」流量,提高可用性的同时,还提升了系统性能。虽然物理上是两个机房,但「逻辑」上还是当做一个机房来用

6、两地三中心是在同城双活的基础上,额外部署一个异地机房做「灾备」,用来抵御「城市」级别的灾害,但启用灾备机房需要时间

7、异地双活才是抵御「城市」级别灾害的更好方案,两个机房同时提供服务,故障随时可切换,可用性高。但实现也最复杂,理解了异地双活,才能彻底理解异地多活

8、异地多活是在异地双活的基础上,任意扩展多个机房,不仅又提高了可用性,还能应对更大规模的流量的压力,扩展性最强,是实现高可用的最终方案

 异地多活入门文章:

干货|搞懂「异地多活」,看这篇就够了 (baidu.com)icon-default.png?t=N7T8https://baijiahao.baidu.com/s?id=1714292603624190772&wfr=spider&for=pc

三 案例  

【分布式】异地多活_三地多活_红衣主教冲不停的博客-CSDN博客

案例一 异地多活没那么难-CSDN博客

挑战1:延时
  异地项目中,跨城市一定会有延时的问题。在中国范围内,延时可能在一百毫秒以内。看起来单次好像没什么,但是像淘宝是个很大的分布式系统,一次页面的展现,背后的交互次数可能在一两百次。如果这一两百次全部是跨城市做的,整个响应时间会增加很多,所以延时带来的挑战非常大。

【解决方案】

  让操作全部在同一机房内完成,即减少数据中心间的通信次数。在并行计算课程中,也提到分布式系统的通信主要分为两部分:本地通信和远程通信。本地通信的开销远远小于远程通信。

  怎样让所有操作在一个机房内完成?单元化。
  所谓单元化,其实就是根据业务特点对所有的业务进行类型划分。出于成本的考量,多活情况下备份全站是不现实的,即:并非所有业务都需要异地多活 。那有哪些服务需要备份呢?

  核心业务。怎么判断一个业务是不是核心业务呢?最简单的依据是流量。比如一个网站提供用户注册、登录、个人信息修改的服务,一天之内,可能有百万级用户登录、十万级用户注册和修改个人信息,那登录就是核心业务。再比如淘宝,那与交易相关的业务肯定也是核心业务,非交易类型的功能就不是核心业务。
  换句话说,核心业务就是流量峰值高的业务(比如双十一的淘宝),这些业务尽管配备了冗余,还是会面临某一时间点访问量暴增的情况,所以需要异地部署。流量小的、用的不多的业务,不用异地部署。
  把一组核心业务单元化的另一个重要依据是:服务间的依赖

  因此在异地部署的并非全站,而是一组业务,这组业务就成为单元,即当前案例中的交易单元。

挑战2:数据一致性
  这是分布式数据库老生常谈的话题,将业务封闭成单元进行异地部署后,如果多地的相同单元同时写一行数据,就会出现冲突。因此在某个时间点,必须确保单行的数据在一个地方写,绝对不能在多个地方写。

【解决方案】

  为了做到这一点,必须确定数据的维度。如果数据只有一个维度,就像Facebook的数据,可以认为只有一个维度,就是用户。但是像淘宝,如果要在淘宝上买一个东西,除了用户本身的信息以外,还会看到所有商品的数据、所有卖家的数据,面对的是买家、卖家和商品三个维度。这时就必须做出一个选择,到底用哪个维度作为唯一的一个封闭的维度。

  单元化时,走向异地的就是买家的核心链路,所以选择了买家这个维度(意思就是卖家和商品在“购买”这个动作发生时其实相对来说是不怎么变动的,访问卖家和商品信息的是买家)。但是这样自然会带来一个问题,因为现在有三个维度的数据,当操作卖家商品数据时,就无法封闭了,因为这时一定会出现需要集中到一个点去写的现象。

实现难点
(一)路由的一致性

  所谓路由一致性,就是通过一个路由算法,保证从用户1进入淘宝、访问前端、前端页面再访问后端服务、后端访问数据库…这一系列操作一定要在同一个单元里。换句话说,这个“单元”并非是像事务一样的东西,它不是在语法规则上强制原子化的,它的“单元化”依赖于人的设计,是设计者通过把一堆东西强制放在同一个地方执行,这堆东西才能变成单元。
  路由一致性,实质就是把这些操作隐式地封装到一起变成单元。如果用户1本来应该进入城市A的数据中心,却因为路由错误进入B城市,那就破坏了这种“单元性”,那数据可能就出错了。同时也能说,相较于同城容灾中使用DNS导流的方法,异地容灾采取的是按用户分流的方法。

(二)数据同步的延时

  这个很好理解,如果多个备份间同步数据的延迟太大就不好了。注意,追求的是最终一致性,而非实时一致性。因为异地延时的影响因素太多了,而且主要是网络上的,比如网线被挖断了、网络故障等等。一个解决方案就是搭建专用的城际骨干网。这个案例要求在全国的范围内,都必须做到一秒内把数据同步完。
  在同步数据的过程中,并非只能使用存储系统的同步功能。MySQL自身的同步功能可以适用于大多数情况,但是在网络波动大、或数据量较大的时候就很慢。因为它的同步是一种单线程的复制,就算对同步过程进行监控,也只能干等着,啥也做不了。而Redis 3.0之前没有Cluster功能,只有主从复制功能,而为了设计上的简单,Redis主从复制有一个比较大的隐患:从机宕机或者和主机断开连接都需要重新连接主机,重新连接主机都会触发全量的主从复制。
  一个好的方法是扩展消息服务,从中间件或者服务层面直接支持跨机房消息同步,将消息体大小控制在10k以下,跨机房消息同步的性能和成本都比较可控。机房间的数据一致性只通过消息同步服务解决,机房内部解决缓存等与消息的一致性问题。跨机房消息同步的核心点在于消息不能丢,一个例子是微博,由于其使用的是MCQ,通过本地写远程读的方式,可以很方便的实现高效稳定的跨机房消息同步。
  二次读取也是好的方法。某些情况下可能出现消息队列同步也延迟了,用户在A中心注册,然后访问B中心的业务,此时B中心本地拿不到用户的账号数据。为了解决这个问题,B中心在读取本地数据失败的时候,可以根据路由规则,再去A中心访问一次(这就是所谓的二次读取,第一次读取本地,本地失败后第二次读取对端),这样就能够解决异常情况下同步延迟的问题。

(三)数据的正确性

  尽管多活成本低,但是以往的异地项目都更倾向于冷备,因为数据的正确性很难保证。数据在多点同时写的时候,一定不能写错。因为数据故障跟业务故障还不一样,跟应用层故障不一样。如果应用出故障了,可能就是用户不能访问。但是如果数据写错了,对用户来说,就彻底乱了。而且这个故障是无法恢复的,因为无法确定到底那里写的才是对的。这是最大的挑战,也是整个方案的第一原则。业务这一层出故障都可以接受,但是不能接受数据故障。

(四)数据的一致性

  一方面,每个单元都需要卖家的数据、商品的数据;另一方面,单元不是全量业务,那一定会有业务需要这个单元,比如说买家在这个单元下了一笔定单,而其他业务有可能也是需要这笔数据,否则可能操作不了,所以需要同步该数据。所以怎样确保每个单元之间的商品、卖家的数据是一致的,然后买家数据中心和单元是一致的,这是非常关键的。还有就是,如果两个多活的中心出现物理上的变动,比如增减机器了,也需要核对数据保证一致性。
————————————————
版权声明:本文为CSDN博主「红衣主教冲不停」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39950496/article/details/121075873

案例二 

饿了么异地多活技术实现(一)总体介绍 - 知乎 (zhihu.com)

与同城多活的方案不同,异地多活的方案会限制机房间的相互调用,需要定义清晰的服务边界,减少相互依赖,让每个机房都成为独立的单元,不依赖于其他机房。(单元化)

  饿了么的服务和地理位置强相关,并且实时性要求高,多活设计最重要的是满足服务的地域性和实时性。

  可用性优先:当发生故障切换机房时,优先保证系统可用,首先让用户可以下单吃饭,容忍有限时间段内的数据不一致,在事后修复。每个机房都会有全量的业务数据,当一个机房失效后,其他的机房可以接管用户。用户在一个机房的下单数据,会实时的复制到其他机房。(服务特性是不同的,只能说在满足服务特性的条件下去让这个多活系统支持一些功能,所以每个多活系统的特点大致相同,但会各有倚重)

主要逻辑
(一)服务划分(Sharding)
  对服务进行分区,让用户,商户,骑手能够正确的内聚到同一个机房覆盖的范围中。分区方案是整个多活的基础,它决定了之后的所有逻辑。根据饿了么的业务特点,自然地选择地理位置作为划分业务的单元,把地理位置上接近的用户,商户,骑手划分到同一个区域,这样一个订单的履单流程就会在一个机房完成,能够保证最小的延时,在某个机房出现问题的时候,也可以按照地理位置把用户,商户,骑手打包迁移到别的机房即可。

  饿了么解决了三个问题:

如果两个城市是接壤的,会出现商家和用户处于不同 ezone 的情况,岂不是破坏了内聚性原则?
——选择更合理的划分地区的方式(基于地理信息的)。
用户是会动的,如果用户从北京到了上海,那么划分规则应该怎么应对?
——这其实就是 ipv4 异地访问的问题,饿了么用 Globa Zone 去解决。这种异地访问肯定会议延迟,大多数情况下是可以接收的。
3.为什么不简单点,按照用户的ID来切分?
——阿里是按照用户ID的取模来划分单元的(案例一),比较简洁,但这个服务强地域性,如果按照ID就会破坏单元化,出现跨机房调用。
(二)流量路由
  基于地理位置划分规则,我们开发了统一的流量路由层(API Router),最基础的分流标签是地理位置,即根据地理分流,当然还考虑了其它的因素。案例一其实就是按照用户去分流的。

(三)数据复制
  为了实现可用优先原则,所有机房都会有全量数据,这样用户可以随时切换到其他机房,全量数据就需要对数据进行实时复制。饿了么开发了相应的中间件,对 mysql,zookeeper ,消息队列和 redis 的数据进行复制。

(四)切换过程和异常保护
  在网络断开,或者是切换过程中,特别容易产生错误数据。比如由于复制延时,订单状态不一致,用户有可能会重复支付。

解决方案:

在网络中断时,如果不是必要,不做切换,因为任意单个机房能够提供完整服务;
如果需要切换,对锁定切换过程中的订单,直到切换完成,数据复制正常,才开放锁定。这个过程也通过 DAL(mysql 中间件)来实现;
对于标记为其他机房的写入数据,DAL 会进行保护,拒绝写入;
DRC (中间件,支持异构数据库实时同步,数据记录变更订阅服务)会检查并报告错误的写入操作,方便检查隐藏问题。
————————————————
版权声明:本文为CSDN博主「红衣主教冲不停」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39950496/article/details/121075873

四 更细致案例分析

看完这篇异地多活的改造,我决定和架构师battle一下 - 知乎 (zhihu.com) 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值