当前位置: 首页 > 科技观察

如何解决分布式系统在跨地域场景下的一致性问题?

时间:2023-03-19 20:06:18 科技观察

跨地域,即所谓“双人异地”、“多人异地”中的异地概念。在业务快速发展的情况下,我们的服务需要跨地域部署,以满足就近访问和跨地域容灾的需求。在这个过程中,跨区域的分布式一致性问题难免会涉及到。跨地域带来的网络延迟问题以及网络延迟衍生的一系列问题,对于设计和构建跨地域的分布式一致性系统来说是一个巨大的挑战。业界有很多解决这个问题的方法。他们都希望解决跨地域场景下的一致性问题。本文分享了阿里巴巴女娲团队在跨地域场景下分布式一致性系统的探索,从“WhatHowFuture”三个方面介绍了跨地域场景、行业通用系统、女娲团队需要承担的需求和挑战思考跨区域场景下的权衡点,以及对跨区域一致性系统未来发展的设计和思考,以发现和解决更多跨区域场景的需求和挑战。一、跨区域需求及挑战1、跨区域需求问题是集团全球化战略下业务快速发展带来的挑战。像淘宝的单元化业务或者速卖通的区域化业务,都有一个无法回避的问题——跨区域的数据读写一致性。其核心需求可以概括为:跨地域业务场景下的跨地域配置同步和服务发现是跨地域一致性协调服务的两个常见业务需求。跨区域部署可以提供就近接入能力,减少服务延迟。根据具体的业务场景,可以分为多区域写入或简化的单区域写入、强一致性读取或最终一致性读取等场景。基于它的跨区域会话管理和跨区域分布式锁也迫切需要提供成熟的解决方案。业务和资源的扩展当一个区域的机房的服务容量达到上限,无法扩展时,需要在一个区域的多个机房中横向、跨区域扩展一个一致的系统。跨区域容灾能力当机房或区域发生灾难性故障时,需要跨区域部署一致的系统,将服务从一个区域快速迁移到另一个区域,完成容灾,实现高可用。2挑战结合网络延迟和业务需求,我们可以总结出跨区域一致性系统需要解决的挑战:延迟:网络延迟可达几十毫秒。多区域部署带来的核心问题是网络延迟高。以区域部署的跨区域集群为例。集群中的机器分别属于杭州、深圳、上海、北京的机房。实际测试从杭州机房到上海的延迟在6ms左右,到深圳和北京的延迟可以达到近30ms。同一机房或同一区域机房的网络延迟一般在毫秒以内,相比跨区域访问的延迟,增加了一个数量级。横向扩展:QuorumServer的规模是有限的。基于Paxos理论及其变体的分布式一致性系统在扩展节点时不可避免地会遇到ReplicationOverhead问题。一般一个Quorum的节点数不超过9个,因此无法简单地将一致的系统节点直接部署在多个区域,需要不断地横向扩展系统以满足业务和资源的扩展需求。存储上限:单节点存储数据有限,故障恢复慢无论是MySQL还是基于Paxos的一致性系统,单节点都会维护和加载全量的镜像数据,这会受到一个节点容量的限制单簇。同时,在failover恢复时,如果数据版本严重落后,需要很长时间才能通过拉取其他区域镜像恢复不可用。2我们的探索1行业解决方案业界对于跨地域一致性系统的设计有很多,主要参考了论文[1]和一些开源的实现。下面介绍几种常见的:跨地域部署图1直接跨地域部署直接跨地域部署,读请求直接读取本地域的节点,速度更快,一致性和可用性由Paxos,并没有单点的问题。缺点也很明显。会遇到第一部分提到的水平扩展问题,即Quorum扩展时会遇到ReplicationOverhead问题。并且随着Quorum节点数量的增加,在极高的跨地域网络延迟下,多数人每次都需要很长时间才能达成共识,写入效率很低。单区域部署+Learner角色图2.引入Learner角色通过引入Learner角色(如ZK中的Observer,etcd的raftlearner[2]),即只进行数据同步,不参与的角色在多数投票中,写请求被转发到某个A区域(图2中的RegionA),避免了直接多节点部署的投票延迟问题。这种方式可以解决水平扩展和延迟的问题,但是由于参与投票的角色都部署在一个区域,当这个区域的机房遇到灾难的时候,写服务就会不可用。这种方式是Otter[3]采用的部署方式。Multi-service+Partition&single-regiondeployment+Learner图3.多个服务处理分区将数据按照规则划分到不同的Partition中。每个地区有一个Quorum提供服务。不同区域的Quorum负责不同的Partition。region之间的Quorums使用Learner区分Partition的数据同步和写请求转发,确保某个区域出现问题只影响该区域Partition的可用性。同时,该方案下会存在正确性问题,即运算不符合顺序一致性[4]问题(见论文[1])。在实际实施中,根据业务场景有多种解决方案,并有针对性地进行优化和权衡,弥补缺陷。业界比较常见的方案是单地域部署+学习者角色的方案,通过同城多活和学习者之间的跨地域数据同步来保证高可用和高效。其他方案也有自己的优化方案。跨区域部署可以通过减少决策时跨区域的通信来减少延迟和带宽问题,例如TiDB的FollowerReplication[5];multi-service+Partition&single-regiondeployment+Learner方案的正确性也可以在论文[1]中描述,在读取前增加一个sync操作,牺牲部分读取的可用性来保证一致性。最终结论如下表所示,后面将对关键项进行详细说明:2跨区域权衡通过第一部分总结的需求挑战和前期对行业跨区域一致性系统解决方案的研究,可以得出,基于Paxos的分布式一致性体系在跨地域场景下的核心权衡点:跨地域写操作通过一致性协议达成决议太慢。该地区的Multi-active在极端情况下无法提供可用性。需要具备分布式系统的核心水平扩展能力。针对这三个问题,我们设计了一个解耦日志镜像的跨区域一致性系统。3跨区域日志镜像解耦图4日志镜像解耦示意图如图3所示,我们的系统分为后端日志同步通道和前端全状态机——日志镜像解耦的架构。后端跨区域全局日志同步通道,负责保证各区域请求日志的强一致性;前端全状态机部署在各个区域,处理客户端请求,同时负责与后端日志服务交互,对外提供全局强一致性的元数据访问服务,可以通过快速修改状态实现接口机器根据业务需要。在全局日志和本地镜像分离的架构下,除了解耦本身带来的系统运维和扩展性的提升之外,我们还可以解决非解耦架构下的很多问题。下面的分析就是基于这个架构下面是如何解决之前考虑过的一些主要问题:写操作的效率。从部署方式来看,好像是类似于直接多region多节点部署,然后在每个region中添加Learner角色。就是直接多节点部署,多节点部署。引入组合Learner,它结合了两种方法的优缺点。最大的区别是我们的日志和镜像是解耦的,也就是说跨地域部分是一个简单的日志同步,足够轻量高效,而且由于每个地域只有一个节点,可以节省跨地域带宽(类似于TiDB的FollowerReplication)。同时,后端的日志同步通道还可以实现多组的功能,将数据划分为Partition,每个一致性组负责不同的Partiton。由于大部分业务场景下的读操作都是读取本地数据,所以各种方式区别不大。主要进行写操作的延时分析。下面是写操作(或者强一致性读)的延时分析:RTT(Round-TripTime),可以简单理解为发送方从发送请求到得到响应所经过的时间。由于跨地域网络延迟较大,下面的RTT主要指跨地域RTT。(1)直接跨区域部署对于一个普通的master共识协议,我们的请求分为两种情况:访问Leader所在区域1个RTT(暂时忽略区域内的小延迟)Client->Leader---->Follower---->Leader->Client访问2RTTClient->Follower---->Leader---->Follower---->Leader---->Follower->(2)中单region部署+Learner同步的方案,region内多active,region间Learner同步,我们的延迟是:0RTTClient->Quorum->Clientregion1RTTClient->Learner---->Quorum---->Learner->Client(3)Multi-servicePartition,single-regiondeployment+Learnersynchronization(类似B结果)写本地域Partition0RTTcross-Partition写1RTT(4)日志镜像解耦架构(类似A)的结果RTTClient->Frontend->LogChannel(local)---->LogChannel(peer)---->LogChannel(local)->Frontend->Clientcross-Partitionwrite2inthelocaldomain分区RTT(Paxos两阶段提交/转发领导者)Client->Frontend->LogChannel(local)---->LogChannel(peer)---->LogChannel(local)---->LogChannel(peer)--->LogChannel(local)->Frontend->Client经过上面的对比可以看出,只要是跨区域一致性协议写操作,至少会有一个RTT延迟,如果PaxosQuorum仅部署在单一区域,无法保证在任何极端情况下的可用性。因此,我们可以根据业务需求来平衡可用性和写入效率。日志镜像解耦架构,可以在多区域部署场景下保证极致的可用性和正确性。当然效率会比单regiondeployment+learner略差。但是采用多积分比直接多区域部署更轻量、更高效,因为Quorum的规模不会因为横向扩展而增加,不会影响投票效率。多服务分区部署方案没有效率优势,但在可操作性、可维护性、正确性、可用性等方面具有优势。一致性跨区域部署和单区域部署+Learner的强一致性得到满足。zookeeper和etcd都有相应的介绍,这里不再赘述。multi-servicePartition和Partition的方案不满足顺序一致性,主要是multi-service不能保证每次写操作commit的顺序,如下图:图5顺序一致性可以看出,当两个Client同时执行x,y修改时,在写操作并发度高的情况下,不能保证顺序一致性。顺序一致性是指每个Client的操作都可以按照正确的顺序排列。在图4的例子中:set1(x,5)=>get1(y)->0=>set2(y,3)=>get2(x)->5或者set2(y,3)=>get2(x)->0=>set2(y,3)=>get1(y)->3都是顺序一致的。日志镜像解耦架构的一致性可以简单理解为跨地域部署+Learner。写操作有sync选项,只有后台日志提交成功并拉取对应的日志才会返回成功,所以必须拉取到本次操作之前其他客户端写操作对应的日志,所以它符合顺序一致性。Availability可用性类似于直接跨地域多节点部署的可用性。前端状态机可以在某个区域的后端节点宕机时转发请求。它还可以在后端全局日志服务不可用时提供读取可用性。提供极端情况下的读写高可用保证。同时,由于镜像保存在各个region的状态机中,当一个前端状态机挂了,客户端可以切换到其他前端,直接从该region拉取数据恢复故障转移恢复时的后端。只需要拉取本域其他前端的图片,不需要跨地域同步图片,可以让前端的不可用时间极短。横向扩展能力横向扩展能力是分布式服务的核心能力。在上述各种方案中,直接跨地域部署的横向扩展能力极差。其他依赖Learner的方法也解决了水平扩展的问题,但是解耦并没有log镜像解耦设计干净。总结比较以上几个关键问题:三个跨区域的可能性更大。在后端日志和前端镜像解耦的状态下,我们对跨地域场景的探索分为两部分——后端日志同步轻量高效和前端状态机灵活富有的。轻量级,体现在架构上,后端只同步日志,带来最小的后端存储压力,只同步轻量级增量日志。效率,体现在后端一致性协议上,是轻量级的,所以只需要考虑投票和选举的逻辑,只关注提高日志同步的效率,不需要消耗后端资源在其他业务逻辑中。灵活,体现在架构上,前端可以自定义上传日志,CAS,事务等都可以打包成日志,供前端解析处理。丰富,主要体现在前端状态机上,因为日志的灵活性给我们留下了很大的探索和构建空间,我们可以根据需求封装一个处理各种复杂事务的状态机。新架构下又出现了新问题。这部分主要探讨如何吸取现有系统的优势,利用日志镜像解耦下的轻量和灵活性,实现跨地域场景下高效丰富的一致性协议和状态机。对跨区域一致性体系的建设也会有思考和规划。总体目标是让后端的一致性协议更精、更深,让前端的状态机做大、做强。1高效的后端一致性协议基于我们之前对写操作效率的讨论,在多个region写入相同数据的场景下,延迟只能控制在2RTT。因为在跨地域场景下,延迟主要是在跨地域的网络通信上。无论是owner-owned转发还是unownedPaxos两阶段提交,延迟都是2RTT。但是如果使用无主协议,比如Paxos的变种EPaxos[6],可以尽可能的提高跨地域场景下的写入效率。延迟分为FastPath和SlowPath。FastPath下延迟为1RTT。SlowPath中的延迟是2RTT。引用EPaxos文章中的一句话:如果并发提出的日志之间没有冲突,EPaxos只需要运行PreAccept阶段提交(FastPath),否则需要运行Accept阶段提交(SlowPath)。与分区操作相比,如果后端一致性协议选择EPaxos,可以保证极端情况下的可用性,并且延迟大多数情况下为1RTT,这是masterless一致性协议在跨地域场景下的优势。主要原因是转发Leader操作的RTT省略了一次。目前我们的系统使用的是最基本的Paxos实现。多区域写场景下的延迟,理论上和master协议没有太大区别。后续开发希望使用EPaxos来加快跨区域场景写操作的效率。由于不需要实现各种业务逻辑,效率是后端一致性协议最大的诉求。当然,它的正确性和稳定性也是必不可少的。对于前端状态机,有丰富的场景可以设计和发挥。.CAS操作在这种架构下实现CAS操作是非常自然的。由于后端只有一致的日志,每次我们请求CAS,自然会有一个commitsequence。让我举一个例子。两个客户端同时写入同一个Key的值:图6CAS操作示意图一开始key的值为0,此时Client1和Client2分别对key并发进行CAS操作CAS(key,0,1)和CAS(key,0,2),当这两个操作同时提交和Commit时,ReplicationLog由于后端Quorum达到解析的顺序必须有顺序,所以这两个并发的CAS操作很自然地转换为顺序执行。当Frontend同步到这两个操作的日志时,会依次将这两个操作应用到本地状态机。自然是CAS(key,0,1)成功,更新的key值为1,而CAS(key,0,2)更新失败,前端会返回给请求对应的客户端是否有CAS请求成功或失败。其原理是将一个并发操作变成一个顺序执行的串行过程,从而避免跨地域场景下的加锁操作。可以想象,如果后端维护一个kv结构的数据,需要加一个跨区域的分布式锁来完成这个操作相对比较繁琐,效率也得不到保证。通过只同步日志,将复杂的计算转移到Frontend,可以灵活构建前端状态机,更好的实现CAS或者更复杂的交易功能(该架构可以参考Pravega的StateSynchronizer[7])。GlobalIDGlobalID是一个常见的需求。分布式系统生成一个唯一的ID。常见的有UUID、雪花算法,或者基于数据库、redis、zookeeper的解决方案。与使用zookeeper的znode数据版本生成GlobalID类似,在这种日志镜像分离架构中,可以使用CAS接口调用生成一个key作为GlobalID,每次都对GlobalID进行原子操作。基于上述CAS设计,在跨地域并发场景下不需要加锁,其用法类似于redis对key进行原子操作。2手表操作订阅功能是分布式协同服务必不可少的,也是最常见的业务需求。以下是zk和etcd的研究成果:目前业界比较成熟的实现订阅通知的分布式协调系统有ETCD和ZooKeeper,我们以这两个系统为例来说明各自的解决方案。ETCD会保存多个历史版本数据(MVCC),通过单调递增的版本号来表示版本的新旧。客户端只要传入自己关心的历史版本,服务端就可以将后续的所有事件推送给客户端。Zookeeper不保存数据的多个历史版本,只保存当前数据状态,客户端无法订阅数据的历史版本,客户端只能订阅当前状态之后的变化事件,所以订阅伴随着读取,服务端将当前的数据发送给客户端,然后推送后续事件。同时,为了防止在故障转移等异常场景下订阅旧数据和事件,客户端会拒绝连接旧数据的服务端(这取决于服务端会在每个请求中返回当前的服务器端全局XID)。以上研究结果中,ETCD比较符合我们的界面设计。目前ETCDv3采用HTTP/2TCP连接复用,手表性能得到提升。由于相同的日志加状态机结构,设计功能主要参考了ETCDv3,借鉴了它如何订阅多个key和返回所有历史事件这两个特点。实现etcd订阅的功能,我们在前端状态机同步解析日志的时候,如果有写日志,kv结构的状态机Store和watch接口专门提供的状态机watchableStore就会同时更新。具体实现可以完全参考etcd,然后根据日志版本号将订阅版本之后的所有历史事件返回给客户端。订阅多个key,同时使用线段树作为watcher的rangekeys存储结构,可以实现watcher对watchrangekeys的通知。3租赁机制在无主系统中实现高效的租赁机制是一个很大的挑战。无主系统中没有Leader,任何节点都可以维护Lease。租约分布在每个节点上。当一个节点不可用时,需要平滑切换到其他节点。在无主系统中实现高效的Lease机制的难点在于如何避免后端共识协议随着Lease数量的增加而产生大量的Lease维护消息,影响系统性能。处理而不经过后端。因此,我们的想法是将客户端和前端的Lease聚合为前端和后端的Lease,这样就可以直接在前端本地处理Lease维护消息。四、结语随着全球化战略的推进,跨区域方面的需求会越来越迫切,跨区域场景的真正痛点也会越来越清晰。希望我们在跨区域方面的研究和探索可以给大家一个思路和参考,我们会在跨区域日志镜像分离的架构下继续探索更多的可能性。相关链接[1]https://www.usenix.org/conference/atc16/technical-sessions/presentation/lev-ari[2]https://github.com/etcd-io/etcd/blob/master/Documentation/learning/design-learner.md[3]https://github.com/alibaba/otter[4]https://zhuanlan.zhihu.com/p/43949695[5]https://zhuanlan.zhihu.com/p/94663335[6]https://zhuanlan.zhihu.com/p/163271175[7]https://github.com/pravega/pravega/blob/master/documentation/src/docs/state-synchronizer-design.MD