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

面试问的是分布式事务(2PC、3PC、TCC),所以这个解释没有错!

时间:2023-03-21 00:57:31 科技观察

漫无边际还记得刚入行开始写Java的时候,第一个接触的项目是国家电网公司的一个业务系统。据说这套系统的研发投入了5亿元,鼎盛时期研发人员一度达到500人。项目采用了当时最流行的ssh(Struts+Spring+Hibernate)框架。典型的三层架构(controller->service->dao)简单粗暴。大家写的代码全部放在一个大项目里。项目文件大小达到数百M,解决代码冲突是当时最大的工作量。然而,戏剧性的是,交接测试当天,五个人同时上线,项目崩盘。..嘿!甲方的愤怒你绝对想不到,项目组所有人的祖宗都被问候过了。说了些没用的话,脑子里却一直想着这件事。如果我不说,我就不高兴。就当笑话听吧。进后台前两天,有个同学公众号留言,说说说分销业务吧,面试挂在这个问题上。如今,随着微服务架构体系的普及,面试题也逐渐开始升级。不再是早些年简单的SSH框架知识和数据结构问题。高并发、高可用、分布式服务治理、分布式文件系统、分布式xxx,反正跟分布式有关系的人都会问一些问题。该项目是否实际使用并不重要。你感觉如何?什么是分布式事务?我们看一下百度上分布式事务的定义:分布式事务是指事务参与者、支持事务的服务器、资源服务器和事务管理器位于不同的分布式系统中。在不同的节点上。嗯~看完后,我更糊涂了。我简单的画个图让大家明白。以订单降库存为例:当系统业务量较小时,“一站式”系统完全可以满足现有需求。业务需求,所有业务共享一个数据库,整个下单过程可能只使用同一个交易下的一个方法操作数据库。这时候,所有的操作都在一个事务中,要么全部提交,要么全部回滚。画面并不粗糙,但随着业务量的不断增长,“一站式”的系统逐渐无法应对庞大的流量,因此需要将数据库分库分表,拆分业务服务(SOA),并且会分离出订单中心、用户中心、库存中心。而这样就造成了业务之间相互隔离,每个业务维护自己的数据库,数据交换只能通过RPC调用来完成。当用户再次下单时,需要同时操作订单DB和库存DB,创建订单并扣除库存。两步操作必须同时成功,否则会造成业务混乱,但是此时我们只能保证自己服务的数据一致性,不能保证调用其他服务的成功,所以为了保证整个订单流程的数据一致性,需要分布式事务的介入。在谈分布式事务之前,先回顾一下事务的基本概念:事务是一个程序执行单元,其中的所有操作要么成功,要么失败。一个事务有四个基本特征,也就是我们常说的(ACID)。原子性:事务是一个不可分割的整体,事务内的所有操作要么成功,要么失败。Consistency(一致性):交易执行前后,数据必须从一种状态到另一种状态是一致的(A给B转钱,不能出现A扣了钱,B却没收到)。隔离性:多个并发事务相互隔离,不能相互干扰。Durability(持久性):事务完成后,对数据库的更改永久保存,不可回滚。上面的知识点都是一遍遍重复的概念,也是面试必须要背的东西。分布式事务解决方案有难点,就一定有解决的办法,聪明的程序员什么都难不倒。XA协议是一种基于数据库的分布式事务协议,分为事务管理器和本地资源管理器两部分。作为全局调度器,事务管理器负责统一排序每个本地资源管理器的提交或回滚。二阶提交协议(2PC)和三阶提交协议(3PC)均源于该协议。现在Oracle、Mysql等数据库都实现了XA接口。1.Two-PhaseCommit(2PC)顾名思义,two-phasecommit就是两阶段提交:第一阶段,准备阶段(投票阶段);第二阶段,提交阶段(执行阶段)。上图来自网络。如有侵权联系,请删除。以下是单一扣除库存的示例。简述两阶段提交(2PC)的原理:前面提到业务服务(SOA)后,一个订单流程会被多个服务调用,每个服务不能保证调用其他服务成功。这时候就需要一个全局角色(coordinator)来协调各个服务(participant)。订单请求通过协调器发出,向每个参与者发送Prepare消息,执行本地数据脚本但不提交事务。如果协调器收到参与者的失败消息或超时,则直接向每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行commit或rollback操作,释放事务处理过程中占用的所有资源,可见2PC实现了所有操作要么全部成功,要么全部失败。两阶段提交(2PC)的缺点两阶段提交看似可以提供原子操作,但它有严重的缺陷。网络抖动导致的数据不一致:协调者在第二阶段向参与者发送提交命令后,一旦发生网络抖动,部分参与者收到并执行提交请求,但其他未收到提交请求的参与者无法执行事务提交。这导致整个分布式系统中的数据不一致。超时导致的同步阻塞问题:2PC中的所有参与节点都是事务阻塞。当一个参与者节点发生通信超时时,其他参与者将被动阻塞并占用资源,无法释放。单点故障风险:由于对协调器的依赖过大,一旦协调器出现故障,所有参与者仍处于资源锁定状态,无法完成事务提交操作。虽然在协调者失效后会重新选举一个协调者,但是不能解决上一个协调者宕机导致参与者阻塞的问题。2.三阶段提交(3PC)三阶段提交(3PC)是两阶段提交(2PC)的升级和优化。3PC在2PC的第一和第二阶段中插入一个准备阶段。保证在最终提交阶段之前各个参与节点的状态是一致的。同时在协调者和参与者中都引入了超时机制。当参与者因为各种原因没有收到协调者的提交请求时,会在不阻塞等待的情况下提交本地事务,解决了2PC的单点故障问题,但是3PC仍然没能从根本上解决数据一致性问题.上图来自网络。如有侵权,联系3PC删除。三个阶段分别是CanCommit、PreCommit、DoCommitCanCommit:协调者向所有参与者发送CanCommit命令,询问是否可以进行事务提交操作。如果都回答是,则进入下一阶段。PreCommit:协调者向所有参与者发送PreCommit命令,询问是否可以进行事务的precommit操作。参与者收到PreCommit请求后,如果参与者成功执行事务操作,则返回Yes响应,进入最后的提交阶段。一旦其中一个参与者向协调器发送No响应,或者协调器由于网络超时没有收到参与者的响应,协调器向所有参与者发送中止请求,参与者接受中止命令执行事务中断。DoCommit:在前两个阶段所有参与者的响应反馈为YES后,协调器向参与者发送DoCommit命令,正式提交事务。如果协调器没有收到参与者发送的ACK响应,它将发送给所有参与者。中止请求命令,执行事务中断。3.补偿交易(TCC)许多初学者总是对TCC、2PC、3PC的概念感到困惑,分不清它们的区别。其实TCC和2PC、3PC一样,只是一种实现分布式事务的解决方案而已。TCC(Try-Confirm-Cancel)也称为补偿交易。TCC和2PC的思想很相似,事务处理过程也很相似,只不过2PC是在DB层面应用的,而TCC在应用层面可以理解为2PC。我们需要编写业务逻辑来实现它。TCC的核心思想是:“对于每一个操作,都要注册一个相应的确认(Try)和补偿(Cancel)”。还拿单扣库存来解释它的三个操作:Try阶段:下单时,使用Try操作扣减库存预留资源。确认阶段:确认业务操作的执行,仅在预留资源的基础上发起采购请求。取消阶段:只要涉及的业务方之一未能预留资源,所有业务资源预留请求将被取消。上图来自网络。如有侵权,请联系TCC删除。缺点:应用侵入性强:TCC是基于业务层面的,所以每次操作都需要三个接口:try,confirm,cancel。开发难度:代码开发量巨大,为保证数据一致性,confirm和cancel接口也必须做到幂等。总结简单介绍了2PC、3PC、TCC的概念。如果您有任何错误,请纠正我。分布式事务一直是面试的热门话题,也是高级Java工程师必备的知识点。