数据不会无故丢失,也不会莫名其妙的增加。性问题直接通过当地交易处理。2、随着时间的推移,用户越来越多,发现有一个Java服务支持不了,于是技术老大决定升级系统。根据系统的业务,拆分单个服务,然后也拆分开发人员。开发人员只开发和维护一个或几个服务中的问题。大家各司其职,相互配合。3.当然,服务拆分不是一蹴而就的。这是一项耗时耗力的巨大工程。大多数系统都经过多轮分裂,然后慢慢形成一个稳定的系统。遵循一个核心思路:先按照整体业务进行一轮拆分,再根据拆分后的业务模块进行细化拆分。4、服务拆分后,用户数有抗拒,但发现数据是在不同的服务访问的,这就引出了一个新的问题:如何保证跨服务器数据的一致性?当然,在一个跨服务的分布式系统中,不仅仅存在这个问题,还有其他一系列的问题,比如:服务可用性、服务容错、服务间调用的网络问题等等。只有数据的一致性问题在这里讨论。5.说到数据一致性,大致可以分为三种:强一致性,弱一致性,最终一致性。强一致性:数据一旦写入,随时可以读取到最新的值。弱一致性:写入一条数据,其他地方读取数据,可能查到的数据不是最新的最终一致性:是弱一致性的变种,根本不追求数据的一致性在系统中的时间。但经过一定时间后,数据最终会达到一致性。从这三种一致性模型可以看出,弱一致性和最终一致性一般是异步冗余的,而强一致性是同步冗余的,异步处理带来更好的性能,但也需要对处理数据进行补偿。同步意味着简单,但也不可避免地降低了系统的性能。2.理论上面提到的数据一致性问题其实就是分布式事务的问题。现在有一些解决办法。相信大家或多或少都见过。下面我就带大家回顾一下。2.1.两阶段提交2PC是一种强一致性设计方案,通过引入事务协调器来协调每个本地事务(也称为事务参与者)的提交和回滚。2PC主要分为两个阶段:1.第一阶段:事务协调器会向每个事务参与者发起一个开始事务的命令,每个事务参与者都会执行一个准备操作,然后回复事务协调器是否准备做完了。但是本地事务不会被提交,但是这个阶段需要锁定资源。2、第二阶段:事务协调器收到各个事务参与者的回复后,对各个参与者的回复进行统计。如果每个参与者都回复“可以提交”,那么事务协调器会发送commit命令,让参与操作者正式提交本地事务,释放所有资源,结束全局事务。但是如果一个参与者回复“拒绝提交”,那么事务协调器发送回滚命令,所有参与者回滚本地事务。全部回滚完成后,释放资源,取消全局事务。事务提交流程事务回滚流程当然这里也提到了2PC存在的问题。一种是同步阻塞,比较消耗性能。另一个是协调器失效的问题。一旦协调器失效,所有参与者将被阻塞处理资源锁定状态。2.2.3PC的三阶段提交主要是在2PC基础上的改进,主要解决2PC的阻塞问题。主要是把2PC的第一阶段分为两步,先准备,然后锁定资源,引入超时机制(也就是会造成数据不一致)。3PC的三个阶段包括:CanCommit、PreCommit、DoCommit。具体细节不再赘述,只说一个核心点:CanCommit期间不锁定资源,除非所有参与者都同意开始锁定资源。2.3.TCC灵活事务与之前的2PC和3PC相比,TCC与两兄弟的本质区别在于它是业务层面的分布式事务,而2PC和3PC是数据库层面的。TCC是三个词的缩写:Try、Confirm、Cancel,也分为这三个进程。Try:尝试,即尝试预留资源并锁定资源Confirm:确认,即执行预留资源,执行失败则重试Cancel:取消,取消预留资源,执行失败则重试从上面如图,TCC对业务的侵入性很强,紧耦合在一起。与2PC、3PC相比,TCC的试用范围更广,可以实现跨数据库、跨系统的分布式事务。缺点是实现这三个步骤需要在业务代码中开发大量的逻辑,并且需要和代码耦合,增加了开发成本。交易日志:在TCC模式下,交易发起者和交易参与者都会记录交易日志(交易状态、信息等)。这个事务日志是整个分布式事务在出现意外情况(宕机、重启、网络中断等)时提交和回滚的关键。幂等性:在TCC的第二阶段,无论是确认还是取消,都需要保证幂等性。一旦由于网络等原因执行失败,将发起连续重试。防挂:由于网络的不可靠性,当出现异常情况时,try请求可能比cancel请求晚到达。cancel可能执行空回滚,但是执行try请求时不会保留资源。2.4、Seata这里就不多说seata了。最常用的一种是AT模式。上次已经一步步分析过了。配置完成后,只需要在事务发起方法中添加@GlobalTransactional注解即可启动全局事务。无业务侵入,低耦合。有兴趣的可以参考之前关于Seata的讨论。3.应用场景知乎之前在一家公司遇到过这样的业务场景;用户通过页面投保并提交订单。订单通过上游服务处理与保单相关的业务逻辑,最终流入下游服务处理绩效、人员晋升、利润分配处理等。对于这种场景,双方处理的业务逻辑不是在同一个服务中,但访问不同的数据库。当涉及到数据一致性问题时,就需要使用分布式事务。对于上面介绍的几种方案,只讨论了理论和思路。我总结一下这个业务场景中使用的一个实现方案。采用本地消息表+MQ异步消息的方案,实现了事务的最终一致性,也符合当时的业务场景。一致性比较强,实现了高性能。下面是解决方案的思路图。真实业务处理可能有多种状态,需要明确哪些状态需要定时任务补偿。需要加入轮次的概念,重试多次后报警,人工干预处理。因为MQ和定时任务的存在,必然会出现重复请求,所以下游一定要做好幂等反重,否则会出现重复数据,导致数据不一致的情况对于落地实现,不要说不多说,直接上代码。首先定义两个表tb_order和tb_notice_message分别存储订单信息和本地交易信息CREATETABLE`tb_order`(`id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'主键id',`user_id`int(11)NOTNULLCOMMENT'Ordererid',`order_no`varchar(255)CHARACTERSETlatin1NOTNULLCOMMENT'ordernumber',`insurance_amount`decimal(16,2)NOTNULLCOMMENT'insuranceamount',`order_amount`decimal(16,2)DEFAULTNULLCOMMENT'Premium',`create_time`datetimeDEFAULTNULLCOMMENT'创建时间',`update_time`datetimeDEFAULTNULLONUPDATECURRENT_TIMESTAMPCOMMENT'更新时间',`is_delete`tinyint(4)DEFAULT'0'COMMENT'删除标志:0-不要删除;1-delete',PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=0DEFAULTCHARSET=utf8mb4;CREATETABLE`tb_notice_message`(`id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'primarykeyid',`type`tinyint(4)NOTNULLCOMMENT'业务类型:1-order',`status`tinyint(4)NOTNULLDEFAULT'1'COMMENT'status:1-pending,2-processed,3-Alert',`data`varchar(255)NOTNULLCOMMENT'Information',`retry_count`tinyint(4)DEFAULT'0'COMMENT'Numberofretries',`create_time`datetimeNOTNULLCOMMENT'Creationtime',`update_time`datetimeDEFAULTNULLONUPDATECURRENT_TIMESTAMPCOMMENT'Updatetime',`is_delete`tinyint(4)NOTNULLDEFAULT'0'COMMENT'删除标志:0-不删除;1-delete',PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=0DEFAULTCHARSET=utf8mb4;处理订单服务,我们可以使用之前提到的装饰器模式,装饰这个服务保存本地交易和发送mq消息,交给装饰器类去做,服务只需要关心业务逻辑,也符合开闭原则。/***@author往事如风*@version1.0*@date2022/12/1310:58*@description*/@Service@Slf4j@AllArgsConstructorpublicclassOrderServiceimplementsBaseHandler