[高可用架构] 异地多活设计辣么难?其实是你想多了!

标签: 架构 设计 其实 | 发表时间:2017-01-20 13:24 | 作者:17080054727
出处:https://www.v2ex.com/

原文链接: http://click.aliyun.com/m/9598/

  1. 引言 有幸参与了阿里游戏的一个高可用方案的设计,并且在网上发表了方案(面向业务的立体化高可用架构设计),后来参加 GOPS 全球运维大会深圳站,与众多行业高手交流,发现大家对“异地多活”这个方案设计非常感兴趣,毕竟“异地多活”的方案价值非常大,尤其是互联网行业,规模稍微大一点几乎都必须是标配;但同时大家都觉得“异地多活”的方案设计又很难,网络、数据、事务等各种问题混杂在一起,很多问题看似是无法解决的。比如说:“网络断了怎么保证数据一致性”、“怎么保证异地事务一致性”、“业务怎么无缝的在多个地点切换”。。。。。。等等。

其实大部分问题我们之前也遇到过,这些问题当时也困扰着我们,后来我们经过讨论和思考,发现其实很多时候我们困扰的主要原因是过于“追求完美的异地多活方案”,这样导致“异地多活”设计中出现很多了的思维误区,而如果不意识到这些思维误区,就会陷入死胡同,导致无法实现真正的“异地多活”方案。

接下来我将总结常见的思维误区,看看你踩中了哪个坑?

  1. 所有业务异地多活 “异地多活”是为了保证业务的高可用,但很多朋友在考虑这个“业务”的时候,会不自觉的陷入一个思维误区:我要保证所有业务的“异地多活”!

比如说假设我们需要做一个“用户子系统”,这个子系统负责“注册”、“登录”、“用户信息”三个业务。为了支持海量用户,我们设计了一个“用户分区”的架构,即:正常情况下用户属于某个主分区,每个分区都有其它数据的备份,用户用邮箱或者手机号注册,路由层拿到邮箱或者手机号后,通过 hash 计算属于哪个中心,然后请求对应的业务中心。基本的架构如下:

9dd2bbb38e092fe164d082aa2bca252646281290

考虑这样一个系统,如果 3 个业务要同时实现异地多活,我们会发现如下一些难以解决的问题:

[注册]

A 中心注册了用户,数据还未同步到 B 中心,此时 A 中心宕机,为了支持注册业务多活,那我们可以挑选 B 中心让用户去重新注册。看起来很容易就支持多活了,但仔细思考一下会发现这样做会有问题:一个手机号只能注册一个账号, A 中心的数据没有同步过来, B 中心无法判断这个手机号是否重复,如果 B 中心让用户注册,后来 A 中心恢复了,发现数据有冲突,怎么解决?实际上是无法解决的,因为注册账号不能说挑选最后一个生效;而如果 B 中心不支持本来属于 A 中心的业务进行注册,注册业务的双活又成了空谈。

有的朋友可能会说:那我修改业务规则,允许一个手机号注册多个账号不就可以了么?

这样做是不可行的,类似一个手机号只能注册一个账号这种规则,是核心业务规则,修改核心业务规则的代价非常大,几乎所有的业务都要重新设计,为了架构设计去改变业务规则,而且是这么核心的业务规则是得不偿失的。

[用户信息]

用户信息的修改和注册有类似的问题,即: A 、 B 两个中心在异常的情况下都修改了用户信息,如何处理冲突?

由于用户信息并没有账号那么关键,一种简单的处理方式是按照时间合并,即:最后修改的生效。业务逻辑上没问题,但实际操作也有一个很关键的坑:怎么保证多个中心所有机器时间绝对一致?在异地多中心的网络下,这个是无法保证的,即使有时间同步也无法完全保证,只要两个中心的时间误差超过 1s ,数据就可能出现混乱,即:先修改的反而生效。

还有一种方式是生成全局唯一递增 ID ,这个方案的成本很高,因为这个全局唯一递增 ID 的系统本身又要考虑异地多活,同样涉及数据一致性和冲突的问题。

综合上面的简单分析,我们可以发现,如果“注册”“登录”、“用户信息”全部都要支持异地多活的话,实际上是挺难的,有的问题甚至是无解的。那这种情况下我们应该如何考虑“异地多活”的方案设计呢?答案其实很简单:优先实现核心业务的异地多活方案!

对于我们的这个模拟案例来说,“登录”才是最核心的业务,“注册”和“用户信息”虽然也是主要业务,但并不一定要实现异地多活。主要原因在于业务影响。对于一个日活 1000 万的业务来说,每天注册用户可能是几万,修改用户信息的可能还不到 1 万,但登录用户是 1000 万,很明显我们应该保证登录的异地多活。对于新用户来说,注册不了影响并不很明显,因为他还没有真正开始业务;用户信息修改也类似,用户暂时修改不了用户信息,对于其业务不会有很大影响,而如果有几百万用户登录不了,就相当于几百万用户无法使用业务,对业务的影响就非常大了:公司的客服热线很快就被打爆了,微博微信上到处都在传业务宕机,论坛里面到处是在骂娘的用户,那就是互联网大事件了!

而登录实现“异地多活”恰恰是最简单的,因为每个中心都有所有用户的账号和密码信息,用户在哪个中心都可以登录。用户在 A 中心登录, A 中心宕机后,用户到 B 中心重新登录即可。

有的朋友可能会问,如果某个用户在 A 中心修改了密码,此时数据还没有同步到 B 中心,用户到 B 中心登录是无法登录的,这个怎么处理?这个问题其实就涉及另外一个思维误区了,我们稍后再谈。

  1. 实时一致性 异地多活本质上是通过异地的数据冗余,来保证在极端异常的情况下业务也能够正常提供给用户,因此数据同步是异地多活设计方案的核心,但我们大部分人在考虑数据同步方案的时候,也会不知不觉的陷入完美主义误区:我要所有数据都实时同步!

数据冗余就要将数据从 A 地同步到 B 地,从业务的角度来看是越快越好,最好和本地机房一样的速度最好,但让人头疼的问题正在这里:异地多活理论上就不可能很快,因为这是物理定律决定的,即:光速真空传播是每秒 30 万公里,在光纤中传输的速度大约是每秒 20 万公里,再加上传输中的各种网络设备的处理,实际还远远达不到光速的速度。

除了距离上的限制外,中间传输各种不可控的因素也非常多,例如挖掘机把光纤挖断,中美海底电缆被拖船扯断、骨干网故障等,这些故障是第三方维护,我们根本无能为力也无法预知。例如广州机房到北京机房,正常情况下 RTT 大约是 50ms 左右,遇到网络波动之类的情况, RTT 可能飙升到 500ms 甚至 1s ,更不用说经常发生的线路丢包问题,那延迟可能就是几秒几十秒了。

因此异地多活方案面临一个无法彻底解决的矛盾:业务上要求数据快速同步,物理上正好做不到数据快速同步,因此所有数据都实时同步,实际上是一个无法达到的目标。

既然是无法彻底解决的矛盾,那就只能想办法尽量减少影响。有几种方法可以参考:

尽量减少异地多活机房的距离,搭建高速网络; 尽量减少数据同步; 保证最终一致性,不保证实时一致性;

[减少距离:同城多中心]

为了减少两个业务中心的距离,选择在同一个城市不同的区搭建机房,机房间通过高速网络连通,例如在北京的海定区和通州区各搭建一个机房,两个机房间采用高速光纤网络连通,能够达到近似在一个机房的性能。

这个方案的优势在于对业务几乎没有影响,业务可以无缝的切换到同城多中心方案;缺点就是无法应对例如新奥尔良全城被水淹,或者 2003 美加大停电这种极端情况。所以即使采用这种方案,也还必须有一个其它城市的业务中心作为备份,最终的方案同样还是要考虑远距离的数据传输问题。

[减少数据同步]

另外一种方式就是减少需要同步的数据。简单来说就是不重要的数据不要同步,同步后没用的数据不同步。

以前面的“用户子系统”为例,用户登录所产生的 token 或者 session 信息,数据量很大,但其实并不需要同步到其它业务中心,因为这些数据丢失后重新登录就可以了。

有的朋友会问:这些数据丢失后要求用户重新登录,影响用户体验的呀! 确实如此,毕竟需要用户重新输入账户和密码信息,或者至少要弹出登录界面让用户点击一次,但相比为了同步所有数据带来的代价,这个影响完全可以接受,其实这个问题也涉及了一个异地多活设计的典型思维误区,后面我们会详细讲到。

[保证最终一致性]

第三种方式就是业务不依赖数据同步的实时性,只要数据最终能一致即可。例如: A 机房注册了一个用户,业务上不要求能够在 50ms 内就同步到所有机房,正常情况下要求 5 分钟同步到所有机房即可,异常情况下甚至可以允许 1 小时或者 1 天后能够一致。

最终一致性在具体实现的时候,还需要根据不同的数据特征,进行差异化的处理,以满足业务需要。例如对“账号”信息来说,如果在 A 机房新注册的用户 5 分钟内正好跑到 B 机房了,此时 B 机房还没有这个用户的信息,为了保证业务的正确, B 机房就需要根据路由规则到 A 机房请求数据(这种处理方式其实就是后面讲的“二次读取”)。

而对“用户信息”来说, 5 分钟后同步也没有问题,也不需要采取其它措施来弥补,但还是会影响用户体验,即用户看到了旧的用户信息,这个问题怎么解决呢?这个问题实际上也涉及到了一个思维误区,在最后我们统一分析。

  1. 只使用存储系统的同步功能 数据同步是异地多活方案设计的核心,幸运的是基本上存储系统本身都会有同步的功能,例如 MySQL 的主备复制、 Redis 的 Cluster 功能、 elasticsearch 的集群功能。这些系统本身的同步功能已经比较强大,能够直接拿来就用,但这也无形中将我们引入了一个思维误区:只使用存储系统的同步功能!

既然说存储系统本身就有同步功能,而且同步功能还很强大,为何说只使用存储系统是一个思维误区呢?因为虽然绝大部分场景下,存储系统本身的同步功能基本上也够用了,但在某些比较极端的情况下,存储系统本身的同步功能可能难以满足业务需求。

以 MySQL 为例, MySQL5.1 版本的复制是单线程的复制,在网络抖动或者大量数据同步的时候,经常发生延迟较长的问题,短则延迟十几秒,长则可能达到十几分钟。而且即使我们通过监控的手段知道了 MySQL 同步时延较长,也难以采取什么措施,只能干等。

Redis 又是另外一个问题, Redis 3.0 之前没有 Cluster 功能,只有主从复制功能,而为了设计上的简单, Redis 主从复制有一个比较大的隐患:从机宕机或者和主机断开连接都需要重新连接主机,重新连接主机都会触发全量的主从复制,这时候主机会生成内存快照,主机依然可以对外提供服务,但是作为读的从机,就无法提供对外服务了,如果数据量大,恢复的时间会相当的长。

综合上述的案例可以看出,存储系统本身自带的同步功能,在某些场景下是无法满足我们业务需要的。尤其是异地多机房这种部署,各种各样的异常都可能出现,当我们只考虑存储系统本身的同步功能时,就会发现无法做到真正的异地多活。

解决的方案就是拓开思路,避免只使用存储系统的同步功能,可以将多种手段配合存储系统的同步来使用,甚至可以不采用存储系统的同步方案,改用自己的同步方案。

例如,还是以前面的“用户子系统”为例,我们可以采用如下几种方式同步数据:

消息队列方式:对于账号数据,由于账号只会创建,不会修改和删除(假设我们不提供删除功能),我们可以将账号数据通过消息队列同步到其它业务中心。 二次读取方式:某些情况下可能出现消息队列同步也延迟了,用户在 A 中心注册,然后访问 B 中心的业务,此时 B 中心本地拿不到用户的账号数据。为了解决这个问题, B 中心在读取本地数据失败的时候,可以根据路由规则,再去 A 中心访问一次(这就是所谓的二次读取,第一次读取本地,本地失败后第二次读取对端),这样就能够解决异常情况下同步延迟的问题。 存储系统同步方式:对于密码数据,由于用户改密码频率较低,而且用户不可能在 1s 内连续改多次密码,所以通过数据库的同步机制将数据复制到其它业务中心即可,用户信息数据和密码类似。 回源读取方式:对于登录的 session 数据,由于数据量很大,我们可以不同步数据;但当用户在 A 中心登录后,然后又在 B 中心登录, B 中心拿到用户上传的 session id 后,根据路由判断 session 属于 A 中心,直接去 A 中心请求 session 数据即可,反之亦然, A 中心也可以到 B 中心去拿取 session 数据。 重新生成数据方式:对于第 4 中场景,如果异常情况下, A 中心宕机了, B 中心请求 session 数据失败,此时就只能登录失败,让用户重新在 B 中心登录,生成新的 session 数据。 (注意:以上方案仅仅是示意,实际的设计方案要比这个复杂一些,还有很多细节要考虑)

综合上述的各种措施,最后我们的“用户子系统”同步方式整体如下: 3d8b9d29d589937c40a266a0fc5b6dd5a0275ffb

  1. 100%可用性 前面我们在给出每个思维误区对应的解决方案的时候,其实都遗留了一些小尾巴:某些场景下我们无法保证 100%的业务可用性,总是会有一定的损失。例如密码不同步导致无法登录、用户信息不同步导致用户看到旧的用户信息等等,这个问题怎么解决?

其实这个问题涉及异地多活设计方案中一个典型的思维误区:我要保证业务 100%可用!但极端情况下就是会丢一部分数据,就是会有一部分数据不能同步,怎么办呢,有没有什么巧妙和神通的办法能做到?

很遗憾,答案是没有!异地多活也无法保证 100%的业务可用,这是由物理规律决定的,光速和网络的传播速度、硬盘的读写速度、极端异常情况的不可控等,都是无法 100%解决的。所以针对这个思维误区,我的答案是“忍”!也就是说我们要忍受这一小部分用户或者业务上的损失,否则本来想为了保证最后的 0.01%的用户的可用性,做个完美方案,结果却发现 99.99%的用户都保证不了了。

对于某些实时强一致性的业务,实际上受影响的用户会更多,甚至可能达到 1/3 的用户。以银行转账这个业务为例,假设小明在北京 XX 银行开了账号,如果小明要转账,一定要北京的银行业务中心是可用的,否则就不允许小明自己转账。如果不这样的话,假设在北京和上海两个业务中心实现了实时转账的异地多活,某些异常情况下就可能出现小明只有 1 万存款,他在北京转给了张三 1 万,然后又到上海转给了李四 1 万,两次转账都成功了。这种漏洞如果被人利用,后果不堪设想。

当然,针对银行转账这个业务,可以有很多特殊的业务手段来实现异地多活。例如分为“实时转账”和“转账申请”。实时转账就是我们上述的案例,是无法做到“异地多活”的;但“转账申请”是可以做到“异地多活”的,即:小明在上海业务中心提交转账请求,但上海的业务中心并不立即转账,而是记录这个转账请求,然后后台异步发起真正的转账操作,如果此时北京业务中心不可用,转账请求就可以继续等待重试;假设等待 2 个小时后北京业务中心恢复了,此时上海业务中心去请求转账,发现余额不够,这个转账请求就失败了。小明再登录上来就会看到转账申请失败,原因是“余额不足”。不过需要注意的是“转账申请”的这种方式虽然有助于实现异地多活,但其实还是牺牲了用户体验的,对于小明来说,本来一次操作的事情,需要分为两次:一次提交转账申请,另外一次要确认是否转账成功。

虽然我们无法做到 100%可用性,但并不意味着我们什么都不能做,为了让用户心里更好受一些,我们可以采取一些措施进行安抚或者补偿,例如:

挂公告:说明现在有问题和基本的问题原因,如果不明确原因或者不方便说出原因,可以说“技术哥哥正在紧急处理”比较轻松和有趣的公告。 事后对用户进行补偿:例如送一些业务上可用的代金券、小礼包等,降低用户的抱怨。 补充体验:对于为了做异地多活而带来的体验损失,可以想一些方法减少或者规避。以“转账申请”为例,为了让用户不用确认转账申请是否成功,我们可以在转账成功或者失败后直接给用户发个短信,告诉他转账结果,这样用户就不用不时的登录系统来确认转账是否成功了。

  1. 一句话谈“异地多活” 综合前面的分析,异地多活设计的理念可以总结为一句话:采用多种手段,保证绝大部分用户的核心业务异地多活!

原文链接: http://click.aliyun.com/m/9598/

相关 [架构 设计 其实] 推荐:

[高可用架构] 异地多活设计辣么难?其实是你想多了!

- - V2EX - 技术
原文链接: http://click.aliyun.com/m/9598/. 比如说:“网络断了怎么保证数据一致性”、“怎么保证异地事务一致性”、“业务怎么无缝的在多个地点切换”. 其实大部分问题我们之前也遇到过,这些问题当时也困扰着我们,后来我们经过讨论和思考,发现其实很多时候我们困扰的主要原因是过于“追求完美的异地多活方案”,这样导致“异地多活”设计中出现很多了的思维误区,而如果不意识到这些思维误区,就会陷入死胡同,导致无法实现真正的“异地多活”方案.

软件架构设计

- - 企业架构 - ITeye博客
软件架构设计尚没有万灵的方法论支持,还是个非常新兴的行业,给出个人理解的行业软件架构设计过程,受个人水平有限,仅供参考:. 1.业务分析:针对目标行业的业务战略、蓝图、业务功能及流程进行分析,提出其中部分功能可以使用信息化进行处理,通过分析可以得出信息化要解决的问题. 2.解决方案设计:根据业务战略,形成行业信息化解决方案.

架构设计-逻辑层

- - 人月神话的BLOG
知乎看到一个问题,也是当前在软件设计开发中普遍存在的一个问题,如下:. 现在要开发一个业务逻辑比较复杂的项目,但是在网上看了设计模式的思想后感觉自己以前写的东西扩展性都不好,接口定义也不合适,都是一个实体类一个接口,项目施工也感觉不合理,感觉项目施工中应该先集中定义好接口,并完成业务逻辑,然后在具体实现接口,不知道这样想是不是正确.

秒杀架构设计

- - IT瘾-dev
最近在部门内部分享了原来在电商业务做秒杀活动的整体思路,大家对这次分享反馈还不错,所以我就简单整理了一下,分享给大家参考参考. 通俗一点讲就是网络商家为促销等目的组织的网上限时抢购活动. 比如说京东秒杀,就是一种定时定量秒杀,在规定的时间内,无论商品是否秒杀完毕,该场次的秒杀活动都会结束. 这种秒杀,对时间不是特别严格,只要下手快点,秒中的概率还是比较大的.

架构设计和概要设计

- - 人月神话的BLOG
初步再来探讨下架构设计和概要设计的区别和边界问题. 架构设计包括了功能性架构和技术架构设计两个部分的内容,功能性架构解决业务流程和功能问题,而技术架构解决非功能性需求等问题. 两种架构都包括了动态和静态两个方面的内容,对于功能性架构中动态部分为业务流程驱动全局用例,用例驱动的用例实现等;对于技术架构中动态部分为架构运行机制,而静态部分为框架,分层等方面的内容.

社区讨论:Android的架构设计

- - InfoQ cn
最近,开发者在知乎社区中就Android的架构设计展开了 讨论. 有人问“Android 架构设计的思想与原则是什么. 最近工作中遇到了Android中的权限问题,发现Android确实是开源的,但并不开放,比如权限控管就相当严格,限制做很多事情,这一点得益于Linux内核. 这也勾起来对其架构研究的兴趣,不知到哪位能够深度剖析下Android架构设计的思想与原则.

分层架构设计原则

- - 博客园_首页
通常一个软件系统都包含不同部分互相交互耦合,我们希望设计能够将系统划分为有意义的各个部件,各个部件能够独立的开发、演进、部署. 这时整体性的设计已经无法满足这些挑战,这就需要我们对系统进行合理清晰的划分. 通常我们为待开发的系统定义多个层次,每一层完成独立的功能. 1:系统分为多层,每层完成独立的功能,层内部继续细分子模块,每层能够独立演进、部署.

CDN架构设计及注意事项

- - ITeye博客
内容传输网络或内容分发网络(CDN)是一个包含数据副本的缓存系统,存在于网络中不同的节点以便可以最大化的利用网络来传输数据至客户端. 一个客户端访问离它最近节点的数据副本,而不是所有的客户端访问相同的中心服务器,因此避免了服务器瓶颈问题. CDN所缓存的内容类型包括web对象、可下载的对象(媒体文件、软件、文档)、应用程序和实时媒体流.

Solr与HBase架构设计 - aitanjupt

- - 博客园_首页
摘要:本篇是本人在做一个大数据项目. ,对于系统架构总结的一点想法,如何在保证存储量的情况下,又能保证数据的检索速度. 前提:      Solr、SolrCloud提供了一整套的数据检索方案,HBase提供了完善的大数据存储机制. 需求:      1、对于添加到HBase中的结构化数据,能够检索出来.

网购秒杀系统架构设计

- - 企业架构 - ITeye博客
秒杀活动只是网站营销的一个附加活动,这个活动具有时间短,并发访问量大的特点,如果和网站原有应用部署在一起,必须会对现有业务造成冲击,稍有不慎可能导致整个网站瘫痪. 用户在秒杀开始前,通过不停刷新浏览器页面以保证不会错过秒杀,这些请求如果按照一般的网站应用架构,访问应用服务器、连接数据库,会对应用服务器和数据库服务器造成极大的负载压力.