当前位置: 首页 > 后端技术 > Java

采访者:亿级流量架构的分布式事务如何实现?我傻眼了,.

时间:2023-04-01 18:21:23 Java

采访者:亿级流量架构的分布式事务如何实现?我傻眼了。.作者:等不及口琴\来源:https://www.cnblogs.com/Coura...分布式事务和分布式锁是分布式事务中的难点。一篇关于分布式事务的文章可能还没写完。我的习惯了,就从基本概念开始,一步步介绍。前面先梳理一下交易中的一些基本概念。如果你对基本概念很清楚,可以直接看《一致性讨论》及后面的部分。方便自己总结回顾,与他人交流分享。什么是分布式事务在日常生活中,很多事情不是做就是不做,不能只做一部分,否则会出现其他复杂的问题。很多人喜欢举个转账的例子。同一个账户,A在湖北转500,B在广东取500,那么A转出后,A账户的钱会被扣除,B账户的金额会增加:交易=(扣除A账户出500,B账户增加500)看,像这样的多个步骤放在一起就形成了一笔交易,要么执行,要么不执行。如果我们的数据存在多个数据库中,也就是存在跨库调用。由于网络的不安全性和延迟性,如何保证交易的分布式执行?如果执行到一半就断电了怎么办?在讲解分布式事务之前,先简单回顾一下事务的一些特性,俗称ACID,下面一一讲解:化学中的原子性(Atomic),分子组成分子是维持化学性质的最小单位,如H2O、CO2H2O、CO2等,由原子组成的物质,原子保持物质性质,像FeFe,意思是不可分割,然后分为质子和中子。我们想到物质,这里的原子性也是一样的原理,就是事务不能拆分。比如上面的事务,看起来可以由两个进程组成,但是如果拆分的话,就不是我们想的那样了。因此,事务不能进一步划分,是原子的。一致性(Consistency)一致性也很好理解。对于以上两个账户,如果银行想知道这里存了多少钱,那么在交易执行之前,A账户有500元,B账户没有钱,银行账户A共有500元。交易执行后,A账户没有钱,B账户有500元。即500元是一定的。不可能A账户有500元,B账户也有500元。那么数据就会不一致。这种情况,说明交易中的某些步骤执行有问题,产生了中间数据,所以不一致。在分布式中,对于一个结果,多个同时查询,结果应该是一致的。隔离(Isolation)当一笔交易未完成时,另一笔交易不会对其产生影响,即如果B转1000给C,则记为交易2:交易1=(500从A账户中扣除,500为加到B账户)交易2=(B账户扣除1000,C账户增加1000)这两笔交易之间不会有任何影响,即从A转出的500元不会到达C账户。持久性(Durability)持久性一般是指数据写入磁盘后不会轻易改变。这里,事务提交后,会影响数据库,不会丢失。这也意味着,随着系统越来越大,为了提高易用性、可维护性、吞吐量等技术指标,即使我们改进原有的架构,解决业务计算问题,数据库仍然会成为整个系统的核心。系统。瓶颈。一致性讨论ACID本质上是为了保护数据的一致性,持久化数据时会触发数据库操作,导致效率低下。因此,围绕一致性(效率)产生了一些讨论,即强一致性、弱一致性和最终一致性。强一致性任何读都可以读到某个数据最后写入的数据。系统中所有进程看到的操作顺序与全局时钟下的顺序一致。简而言之,在任何时刻,所有节点中的数据都是相同的,这就要求数据一有变化就写入数据库。弱一致性数据更新后,不需要及时写入数据库并同步到所有节点,即此时数据可能与真实数据存在一些出入。对于架构,如果可以容忍后续访问,则只能访问部分或全部,如果不能访问,就是弱一致性。最终一致性并不能保证任何时候任何节点上的同一条数据都是相同的,即有的节点数据可能是准确的,有的可能不准确,但是随着时间的迁移,不同节点上的同一份数据总是朝着趋同的方向变化。简单的说,经过一段时间后,节点之间的数据最终会达到一致的状态。三种一致性中,强一致性数据更可靠,但由于需要所有数据库时刻保持数据一致性,效率较低。如果数据不统一,则无法响应请求。在高并发场景下,体验不是很好,所以在实际使用中,根据不同的业务选择,一致性是不同的。购物时,账户支付一定要强一致性,但商品库存数据不一定要强一致性。至于产品下面的评论,甚至可以选择Weakconsistency。前面分库分表中提到的集群的AKF分裂原理(Redis集群分裂原理的AKF),大致意思是硬件性能受上限限制。当硬件无法支持请求流量时,可以将流量分发到不同的服务器上。上面AKF拆分的Y轴和Z轴拆分是业务拆分和数据拆分,也会涉及到将数据库中的数据拆分存储在不同的地方,称为分库分表.类型数据存储在不同的数据库中进行多机存储和加载,使得传统的事务机制ACID无法正常运行。分库分表的内容是数据分片(Sharding),以及分片后数据的定位和整合。具体来说,数据切分就是将数据分散存储在多个数据库中,从而减少单个数据库的数据量,通过扩展主机数量来缓解单个数据库的性能问题,从而达到目的提高数据库的运行性能。数据切分按其切分类型可分为垂直(纵向)切分和水平(横向)切分两种方式。垂直拆分垂直拆分通常分为数据库垂直分区和表垂直分区两种,两者含义相似。垂直分片是将关联度低的不同表按照业务耦合度存放在不同的数据库中。其做法类似于将一个大系统拆分成多个小系统,按业务分类独立划分。类似于“微服务治理”的方式,每个微服务使用一个单独的数据库。如图:竖表类似,比如一个表包含了一个人的所有信息,比如姓名,身份证,性别,身高,体重,省,市,区,村,职业,G点等,那么可以拆成三张表:第一张表只包含基本信息(姓名、身份证、性别、身高、体重);第二张表包含来源信息(省、市、区、村);第三张表包含学习信息(专业,G点)。垂直拆分的优缺点垂直拆分的优点:解决业务系统层面的耦合,业务清晰度类似于微服务的治理,也可以对不同业务的数据进行分级管理、维护、监控、扩展-并发场景,VerticalSegmentation在一定程度上改善了IO、数据库连接、单机硬件资源等瓶颈。垂直隔离的缺点:有些表无法join,只能通过接口聚合来解决,增加了开发的复杂度。分布式事务处理还是复杂单一表数据过多的问题(需要横向拆分)数据库可以水平拆分:上面的水平拆分是按照ID范围进行拆分的。例如:将userId为1~10000的记录分配到第一个图书馆,将userId为10001~20000的记录分配到第二个图书馆,依此类推。从某种意义上说,一些系统采用的“冷热数据分离”,将一些使用较少的历史数据迁移到其他库中,只提供业务功能的热数据查询,也是一种类似的做法。除了上面的按照用户ID范围拆分,还可以做Hash运算拆分,这里不做详细展开。水平拆分的优缺点水平拆分的优点是:单表大小可控,横向扩展自然方便。如果以后要扩展整个sharding集群,只需要增加节点,不需要从其他shards迁移数据。在对分片字段进行范围搜索时,连续分片可以快速定位分片进行快速查询,有效避免跨分片查询的问题。水平拆分的缺点:热点数据成为性能瓶颈。连续分片可能会有数据热点,比如按时间字段分片。有些分片存储的是最近一段时间的数据,可能会被频繁读写,而有些分片存储的是很少被查询到的历史数据。分表带来的问题分库分表可以有效缓解单机单库带来的性能瓶颈和压力,突破网络IO、硬件资源、连接数等瓶颈,同时也带来一些问题。前面提到,一个事务包含一组子操作,这些子操作要么全部执行,要么根本不执行,但是分库之后,一个事务可能涉及多个数据库或者多个表来扩展数据库,网络是不稳定,即交易难以执行为了将分表分库之后的交易与传统交易区分开来,称为分布式交易(cross-shardtransaction)。跨分片交易也是分布式交易,没有简单的解决方案。一般可以使用“XA协议”和“两阶段提交”进行处理。分布式事务可以最大化数据库操作的原子性。但提交交易时需要多个节点协同,延误了提交交易的时间点,延长了交易的执行时间。这导致事务访问共享资源时发生冲突或死锁的可能性更高。随着数据库节点的增多,这种趋势会越来越严重,从而成为系统在数据库层面横向扩展的桎梏。最终一致性对于那些对性能要求高但对一致性要求不高的系统,往往不需要系统的实时一致性。只要在允许的时间段内达到最终一致性,就可以使用事务补偿。不同于交易执行出错后立即回滚的方式,交易补偿是一种事后检查和修复的措施。一些常见的实现方式包括:数据的对账校验、基于日志的比对、定期与标准数据源比对同步等。交易补偿也要结合业务系统来考虑。分布式事务的解决思路在讲这个之前,需要先简单回顾一下CAP原理和Base理论,因为分布式事务不同于ACID刚性事务。在分布式场景下,基于BASE理论,提出了灵活事务的概念。如果想通过灵活的事务实现最终一致性,需要依赖一些特性,这些特性在具体的方案中不一定能满足,因为不同的方案有不同的要求;但如果他们不满意,就不可能做灵活的业务。CAP原理CAP一般人可能听过不下一百次。很多人说CAP是三选二的关系,让人误以为有AC这种情况,但实际上CAP是二??选一的关系。这是在2012年,2009年,已经有论文解释:CAPTwelveYearsLater:Howthe"Rules"HaveChanged相当于修改了之前的三选二说法。CAP中的P(partitionfaulttolerance)是必须的。在满足P的前提下,很难同时满足A(availability)和C(consistency),但是之后又有一篇文章:Harvest,yield,andscalabletolerantsystems,这篇文章是基于上面《CAP12yearslater》论文写的,主要是提出Harvest和Yield的概念,把上面论文讨论的东西讨论得比较仔细。简单的说,满足P后,放宽约束后C和A可以兼顾,不是非此即彼的关系。为什么需要P?为什么CAP原则中需要分区容忍度?首先,我们必须了解什么是分区容忍度。分区是指网络。网络集群设计有许多服务器。在某个时刻,网络不稳定。那么就相当于把网络分成了不同的区域。假设它分为两个区域。此时如果有交易:向第一区发送消息:A转100元给B,向第二区发送消息:A向B转200元那么对于两个分区,有两种情况:a)noavailability,即两个交易中至少有一个不会被接受;b)没有一致性,一半看到的是A转100元给B,另一半看到A转200元给B,所以分区容差一定要满足。解决策略是将一个数据项复制到多个节点。发生分区后,可以将数据项分布到各个区域。容忍度增加。基础理论在很多情况下,我们不需要强一致性系统,所以后来人们争论数据一致性和可用性时,主要关注ACID的强一致性或者BASE的最终一致性,BASE是CAP的结果一致性和可用性之间的权衡来自于对大型互联网分布式系统实践的总结,是基于CAP法则的逐步演进。其核心思想是即使无法实现强一致性,各个应用也可以根据自己的业务特点,以合适的方式实现最终一致性。BASE理论是BasiclyAvailable、SoftState和EventuallyConsistent这三个短语的首字母缩写。基本可用的假设系统出现不可预测的故障,但仍然可以使用。与正常系统相比:响应时间的损失:正常情况下,搜索引擎在0.5秒内将结果返回给用户,而基本可用的搜索引擎可以在2秒内返回结果。功能损失:在电子商务网站上,正常情况下,用户可以顺利完成每一个订单。但在促销期间,为了保障购物系统的稳定性,部分消费者可能会被引导至降级页面。这称为基本可用软状态。相对于原子性,要求多个节点的数据副本是一致的。这是一个“硬状态”。软状态是指:允许系统中的数据以一种中间状态存在,并认为这种状态不影响系统的整体可用性,即允许系统在多个不同的数据副本中存在数据延迟节点。最终一致性提到了软态,然后不可能一直是软态,必须有一个时间限制。在截止日期之后,要保证所有的副本保持数据的一致性,从而达到数据的最终一致性。这个时间限制取决于网络延迟、系统负载、数据复制方案设计等因素。Base的核心思想是:既然无法实现强一致性(Strongconsistency),各个应用可以根据自己的业务特点采用合适的方法使系统实现最终一致性(Eventualconsistency)。有了Basetheory,我们就可以开始描述分布式事务的处理思路了。两阶段提交协议两阶段提交(2PC:Two-PhaseCommit),顾名思义,将一个分布式事务过程拆分为两个阶段:投票和事务提交。为了让整个数据库集群能够正常运行,协议指定了一个单点协调器来协调整个数据库集群的各个节点的运行。为了简化描述,我们将数据库集群中的每个节点都称为参与者。三阶段提交协议还包含了协调者和参与者这两个角色的定义,后面会讲到。第一阶段:投票这个阶段的主要目的是找出数据库集群中的每个参与者是否可以正常执行事务。具体步骤如下:协调者向所有参与者发送交易执行请求,等待参与者反馈交易执行结果;交易参与者收到请求后,执行交易但不提交,并记录交易日志;参与者向协调器报告自己的事务执行状态,并阻塞等待协调器的后续指令。第二阶段:交易提交在第一阶段收到协调者的询问后,每个参与者都会回复自己交易的执行情况。这时候有三种可能:所有参与者都回复可以正常执行交易。一位或多位参与者回复交易执行失败。协调器等待超时。对于第一种情况,协调器会向所有参与者发送提交事务的通知,具体步骤如下:协调器向每个参与者发送提交通知,请求提交事务;参与者收到事务提交通知后执行commit操作,然后释放占用的资源;参与者将事务提交结果信息返回给协调者。对于第二种和第三种情况,协调者认为参与者无法成功执行交易。为了整个集群数据的一致性,需要向每个参与者发送事务回滚通知。具体步骤如下:协调器向每个参与者发送事务回滚通知,请求事务回滚;参与者收到事务回滚通知后执行回滚操作,然后释放占用的资源;参与者将事务回滚结果信息返回给协调者。两阶段提交协议解决了分布式数据库中数据强一致性的问题。在实际应用中,更多的是用来解决事务操作的原子性。下图描述了协调者和参与者之间的状态转换。从协调者的角度来看,发起投票后,进入WAIT等待状态,等待所有参与者回复各自的交易执行状态,收到所有参与者的回复后,下一步决定是否发送commit提交或rollback回退信息。从参与者的角度来看,在回复协调者的投票请求后,会进入READY状态(可以正常执行交易),然后等待协调者的最终决定通知。一旦收到通知,就可以根据决策操作执行提交或回滚。两阶段提交协议原理简单,易于实现,但缺点也很明显,包括:单点问题协调器在整个两阶段提交过程中起着举足轻重的作用,一旦服务器所在的位置协调器所在的节点宕机,会影响整个集群的数据库正常运行。例如,在第二阶段,如果协调器由于故障而未能发送事务提交或回滚通知,则参与者将一直处于阻塞状态,整个数据库集群将无法提供服务。同步阻塞两阶段提交执行过程中,所有参与者都需要服从协调者的统一调度,期间被阻塞,不能进行其他操作,效率极低。数据不一致虽然两阶段提交协议是为分布式数据的强一致性而设计的,但仍然存在数据不一致的可能性。例如,在第二阶段,假设协调者发送事务提交通知,但由于网络问题,该通知仅被部分参与者收到并执行了提交操作,其余参与者因未收到而被阻塞收到通知,则出现数据不一致。通过引入超时机制和相互查询机制,可以在很大程度上解决上述问题。对于超时机制,如果协调器在规定的时间内没有收到所有参与者的响应,它可以自动退出WAIT状态,并向所有参与者发送回滚通知。对于参与者来说,如果处于READY状态,但在规定的时间内没有收到协调器的二阶段通知,则不能随意执行回滚操作,因为协调器可能会发送commit通知,回滚会被执行这时候导致数据不一致。相互查询机制此时,我们可以介入相互查询机制,让参与者A查询其他参与者B的执行情况。如果B执行回滚或提交操作,A可以大胆执行与B相同的操作;如果此时B还没有达到READY状态,可以推断协调器必须发送回滚通知;如果B也处于READY状态,那么A可以继续询问其他参与者。只有当所有参与者都处于READY状态时,此时无法处理两阶段提交协议,会陷入长期阻塞状态。Three-PhaseCommitProtocol三阶段提交协议(3PC:Three-PhaseCommit),针对两阶段提交存在的问题,三阶段提交协议引入了预查询阶段和超时策略,以减少阻塞时间整个集群,提高系统性能。三阶段提交的三个阶段分别是:预查询(can_commit)、预提交(pre_commit)、事务提交(do_commit)。第一阶段:预询在这个阶段,协调者会询问每个参与者是否可以正常执行交易,参与者会根据自己的情况回复一个估计值。与真正执行事务相比,这个过程是轻量级的,具体步骤如下:协调器向每个参与者发送事务查询通知,询问是否可以执行事务操作,并等待回复;每个参与者根据自己的情况回复一个估计值,如果估计可以正常执行交易则返回确认消息,并进入就绪状态,否则返回否定消息。第二阶段:预提交该阶段的协调员会根据第一阶段的查询结果采取相应的行动。查询结果主要分为三种:所有参与者返回确认信息。一名或多名参与者返回负面信息。协调器等待超时。对于第一种情况,协调器会向所有参与者发送交易执行请求,具体步骤如下:协调器向所有交易参与者发送交易执行通知;参与者收到通知后执行交易但未提交;参与者将交易执行返回给客户端。在上述步骤中,如果参与者等待超时,交易将中止。对于第二种和第三种情况,协调器认为事务不能正常执行,于是向各个参与者发出中止通知,请求退出准备状态。具体步骤如下:协调器向所有事务参与者发送中止通知;参与者在通知后收到中断交易。第三阶段:事务提交如果第二阶段事务没有被中断,那么这个阶段的协调器会根据事务执行返回的结果来决定提交或者回滚事务,分为三种情况:所有参与者可以正常执行交易。一位或多位参与者未能执行交易。协调器等待超时。对于第一种情况,协调器向每个参与者发起事务提交请求,具体步骤如下:协调器向所有参与者发送事务提交通知;所有参与者收到通知后执行commit操作,释放占用的资源;参与者向协调者报告事务提交结果。对于第二种和第三种情况,协调者认为事务不能成功执行,于是向每个参与者发送事务回滚请求。具体步骤如下:协调者向所有参与者发送交易回滚通知;所有参与者收到通知后执行回滚操作,释放占用的资源;参与者将事务回滚结果报告给协调器。在这个阶段,如果参与者由于协调器或网络问题而无法收到协调器的提交或回滚请求,则参与者不会像两阶段提交那样被阻塞,而是等待超时后继续提交。两阶段提交,虽然减少了同步阻塞,但无法完全避免数据不一致。两阶段提交协议长期阻塞的概率还是很低的,所以虽然三阶段提交协议在数据一致性方面比两阶段提交协议更安全,但是由于效率问题,两阶段提交协议commitprotocol在实际系统中,更受青睐。TCC模式TCC是Try、Confirm和Cancel的首字母缩写。他们各自的职责是:Try:负责预留资源(比如新建一个status=PENDING的订单);做业务检查,简单来说,不能预留已经占用的资源;隔离预留资源。Confirm:负责为登陆预留的资源使用try阶段预留的资源实际执行业务,幂等。Cancel:负责取消预留资源,用户需要根据自己的业务场景,实现Try、Confirm、Cancel三个操作;事务发起者第一阶段执行Try方法,第二阶段提交并执行Confirm方法,第二阶段回滚方法执行Cancel方法。关于预留资源,我想多说几句。资源是有限的,所以预留的资源是时效性的。如果预留资源长时间没有确认——我们称这种情况为超时——参与方将自行取消。也就是说,参与者具有自我管理资源的能力,可以避免因发起者的问题而长期占用资源。TCC增加了业务检查和撤销交易的功能。同时,TCC将2PC数据库层面的动作升级到服务层面。不同的是TCC的所有动作都是一个本地事务,每个本地事务在动作完成后提交到数据库:Try相当于2PC的Commit请求阶段,加上业务校验逻辑Confirm相当于commit动作2PC的Commit阶段。Cancel相当于2PC的Commit阶段的回滚动作。流程步骤:发起者向所有参与者发送Try,每个参与者执行Try,预留资源的发起者收到所有参与者Try的发起者向所有参与房间发送Confirm/Cancel。每个参与者执行确认/取消发起者收到所有参与者的确认/取消结果。该过程与两阶段提交非常相似。近期热点文章推荐:1.1,000+Java面试题及答案(2021最新版)2.别在满屏的if/else中,试试策略模式,真的很好吃!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.5发布,深色模式太炸了!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!

猜你喜欢