最近看了几篇关于分布式事务的博文,做了一些笔记,总结了这篇文章。图片来自Pexels数据库事务数据库事务(简称:事务),是数据库管理系统执行过程中的一个逻辑单元,由有限的数据库操作序列组成。这些操作要么全部执行,要么不执行,是一个不可分割的工作单元。数据库事务的几个典型特征:原子性(Atomicity)一致性(Consistency)隔离(Isolation)持久性(Durabilily)的缩写是ACID:Atomicity:事务作为一个整体执行,包括对数据库的操作要么全部执行,要么不执行被执行。一致性:表示在事务开始前和事务结束后数据都不会被破坏。如果A账户向B账户转账10元,无论成功与否,A和B的总金额不变。隔离性:当多个事务并发访问时,事务之间是相互隔离的,即一个事务不影响其他事务的运行效果。简而言之,就是事与愿违,水到渠成。持久化:事务完成后,事务对数据库的操作改变会持久保存在数据库中。事务的实现原理本地事务传统的单服务器、单关系数据库事务都是本地事务。本地事务由资源管理器管理,JDBC事务是一种非常典型的本地事务。事务日志InnoDB事务日志包括redolog和undolog。重做日志(redolog):通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行是如何被修改的。用于恢复提交的物理数据页。Undolog(回滚日志):是逻辑日志,不同于redolog记录的物理日志。可以认为,当一条记录被删除时,undolog中会记录一条对应的insert记录,当一条记录被更新时,会记录一条对应的update记录。事务的ACID特性的实现思路:原子性:利用undolog来实现。如果事务执行过程中出现错误或者用户执行了回滚,系统会通过undolog日志返回事务的状态。持久化:使用redolog来实现,只要redolog日志是持久化的,当系统崩溃时,可以通过redolog恢复数据。隔离性:通过锁和MVCC,事务之间相互隔离。一致性:一致性是通过并发下的回滚、恢复、隔离来实现的。分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器和事务管理器分别位于不同分布式系统的不同节点上。简单的说,分布式事务是指分布式系统中的事务,它的存在是为了保证不同数据库节点的数据一致性。为什么我们需要分布式事务?接下来分两个方面进行讲解:微服务架构下的分布式事务随着互联网的飞速发展,轻量级、功能分工明确的微服务走上了历史舞台。比如一个用户下单购买直播礼物的服务,拆分成三个服务,分别是coinService、orderService、giftService。这些服务部署在不同的机器(节点)上,相应的数据库(金币库、订单库、礼物库)也在不同的节点上。当用户下单购买礼物时,礼物库、金币库和订单库在不同的节点上。无法使用本地事务,那么如何保证不同数据库(节点)上的数据一致性呢?这就需要分布式事务!分库分表下的分布式事务随着业务的发展,数据库中的数据越来越大,超过千万级的数据,我们需要对其进行分库分表(公司之前用的是Mycat分库分表,后来用的是Sharding-JDBC)。在子数据库中,数据分布在不同的节点上。比如有的在深圳机房,有的在北京机房~如果要用本地事务来保证,已经无所谓了~还是需要分布式事务。比如A转10元给B,A的账户数据在北京机房,B的账户数据在深圳机房。过程如下:CAP理论&BASE理论学习分布式事务,当然需要了解CAP理论和BASE理论。CAP理论CAP理论作为分布式系统的基础理论,是指在分布式系统中,Consistency(一致性)、Availability(可用性)、Partitiontolerance(分区容错),这三个要素只能同时实现最多两点。一致性(C,Consistency):一致性是指数据在多个副本之间能否保持一致。例如,某个分区节点上更新了一条数据后,从其他分区节点读取的数据也是更新后的数据。可用性(A:Availability):可用性是指系统提供的服务必须始终可用,用户的每次操作请求总能在限定的时间内返回结果。这里的重点是“限时”和“返回结果”。分区容忍度(P,Partitiontolerance):当分布式系统遇到任何网络分区故障时,仍然需要能够对外提供满足一致性和可用性的服务。BASE理论BASE理论是AP在CAP中的延伸。对于我们的业务系统,我们考虑牺牲一致性来换取系统可用性和分区容错性。BASE是BasiclyAvailable(基本可用)、SoftState(软状态)和EventuallyConsistent(最终一致性)这三个词组的缩写。基本可用:基本可用。这是通过支持本地故障而不是系统范围的故障来实现的。例如,如果用户被分区在5个数据库服务器上,一个用户数据库的故障只会影响这台特定主机上20%的用户,而其他用户不会受到影响。SoftState:柔软的状态。状态可能会暂时不同步。EventuallyConsistent:最终一致。最终数据一致就够了,而不是时不时的保持强一致性。分布式事务的几种解决方案分布式事务解决方案主要有以下几种:2PC(two-phasecommit)方案TCC(Try,Confirm,Cancel)本地消息表besteffortnotificationSagatransactiontwo-phasecommitschemetwo-phasecommitScheme是一种比较常用的方案使用分布式事务解决方案。事务的提交分为两个阶段:准备阶段和执行计划的提交。成功的两阶段提交:在准备阶段,事务管理器向各个资源管理器发送准备消息,如果资源管理器的本地事务操作执行成功,则返回成功。在commit执行阶段,事务管理器如果收到所有资源管理器的成功消息,就会向各个资源管理器发送commit消息,RM根据TM的指令执行commit。如图:两阶段提交失败:在准备阶段,事务管理器向各个资源管理器发送准备消息。如果资源管理器的本地事务操作执行成功,则返回success,执行失败则返回failure。在提交执行阶段,如果事务管理器收到任何资源管理器失败的消息,它会向每个资源管理器发送回滚消息。资源管理器根据事务管理器的指令回滚本地事务操作,释放所有事务进程使用的锁资源。2PC方案实现简单,成本低,但主要有以下缺点:单点问题:如果事务管理器失效,资源管理器会一直处于锁定状态。性能问题:事务提交阶段所有资源管理器都处于同步阻塞状态,占用系统资源,直到提交完成才释放资源,容易造成性能瓶颈。数据一致性问题:如果有的资源管理器收到了提交的消息,有的没有收到,就会导致数据不一致。TCC(补偿机制)TCC采用补偿机制,其核心思想是:对于每一个操作,都必须注册一个相应的确认和补偿(撤销)操作。TCC(Try-Confirm-Cancel)通过分解业务逻辑实现分布式事务。对于一个具体的业务服务,TCC分布式事务模型需要业务系统实现以下三个逻辑:Try阶段:尝试执行,完成所有业务的一致性校验,并预留必要的业务资源。Confirm阶段:该阶段确认并提交业务,不做任何检查,因为Try阶段已经检查过了,Confirm阶段默认不会出错。Cancel阶段:如果业务执行失败,进入该阶段,释放Try阶段占用的所有业务资源,回滚Confirm阶段执行的所有操作。TCC分布式事务模型包括以下三部分:主业务服务:主业务服务负责发起和完成整个业务活动。从业务服务:从业务服务是整个业务活动的参与者,实现主业务服务调用的Try、Confirm、Cancel操作。业务活动管理器:业务活动管理器对整个业务活动进行管理和控制,包括记录交易状态,从业务服务中调用Confirm操作,从业务服务中调用Cancel操作等。我们以用户下单购买礼物为例举例模拟TCC实现分布式交易的过程:假设用户A余额为100金币,有5件礼物。A花费10金币下单,购买10朵玫瑰。余额、订单、礼物都在不同的数据库中。TCC的Try阶段:生成订单记录,订单状态为待确认。更新用户A账户金币余额为90,冻结金币为10(预留业务资源)。设置用户赠送数量为5,预增数量为10。Try成功后,进入Confirm阶段。Try过程出现异常,进入Cancel阶段。TCC确认阶段:订单状态更新为已付款。更新用户余额为90,可冻结为0。用户赠送数量更新为15,前置增量为0。如果Confirm过程中出现异常,则进入Cancel阶段。如果Confirm流程执行成功,则交易结束。TCC取消阶段:修改订单状态为已取消。将用户余额更新回100。将用户赠送数量更新为5。TCC方案允许应用自定义数据库操作的粒度,减少锁冲突,提高性能。但也存在以下不足:应用程序侵入性强,Try、Confirm、Cancel三个阶段都需要业务逻辑实现。需要根据网络、系统故障等不同的故障原因,实施不同的回滚策略,实施难度大。一般使用TCC开源框架,ByteTCC,TCC-transaction,Himly。本地消息表eBay最初提出本地消息表是为了解决分布式事务的问题。业界目前较多采用这种方案,其核心思想是将分布式事务拆分成本地事务进行处理。可以看一下基本实现流程图:基本实现思路如下:消息的发送方:需要有一个消息表,记录消息状态的相关信息。业务数据和消息表在同一个数据库中,即必须在同一个本地事务中。在本地事务处理完业务数据并写消息表操作后,将消息写入MQ消息队列。消息将发送给消息消费者。如果发送失败,将重试。消息消费者:处理消息队列中的消息,完成自己的业务逻辑。此时如果本地事务处理成功,说明处理成功。如果本地事务失败,则重试执行。如果是业务失败,则向消息生产者发送业务补偿消息,通知回滚等操作。生产者和消费者定期扫描本地消息表,重新发送未处理的消息或失败的消息。如果有靠谱的自动对账和补货逻辑,这个方案还是很实用的。优缺点:该方案的优点是很好的解决了分布式事务问题,实现了最终一致性。缺点是消息表会和业务系统耦合。最大努力通知什么是最大通知?BestEffortNotification也是一种分布式事务解决方案。以下为企业网银转账示例:企业网银系统调用前台接口,跳转到转账页面。企业网银调用转账系统接口。转账系统完成转账流程,并向企业网银系统发起转账结果通知。如果通知失败,传输系统将根据策略重复通知。如果企业网银系统没有收到通知,会主动调用转账系统接口查询转账结果。遇到汇款退款等情况,转账系统会定期回来进行对账。尽力而为通知方案的目标是发起通知者使用一定的机制尽量将业务处理结果通知给接收者。Best-effort通知的实现机制如下:Best-effort通知方案:实现best-effort通知,可以使用MQ的ACK机制。方案如下:发起者向MQ发送通知。通知程序侦听MQ消息。接收方收到报文后,完成业务,回复ACK。如果接收方没有回应ACK,MQ会以1min、5min、10min等间隔重复通知,通知的接收方可以使用消息校对接口来保证消息的一致性。转账业务实现流程图:交互流程如下:用户请求转账系统转账。转账系统完成转账,并将转账结果发送给MQ。企业网银系统监控MQ,收到转账结果通知。如果没有收到消息,MQ会重复发送通知。收到转账结果后,更新转账状态。企业网银系统还可以主动查询转账系统的转账结果查询界面,更新转账状态。Saga交易Saga交易由普林斯顿大学的HectorGarcia-Molina和KennethSalem提出。它的核心思想是将一个长事务拆分成多个本地短事务,由Saga事务协调器协调。如果正常结束,则正常完成。如果某个步骤失败,将以相反的顺序调用一次补偿操作。Saga简介:Saga=LongLiveTransaction(LLT,长寿交易)。LLT=T1+T2+T3+...+Ti(Ti为本地做空交易)。每个本地事务Ti都有相应的补偿Ci。Saga的执行顺序:正常情况:T1T2T3...Tn异常情况:T1T2T3C3C2C1Saga两种恢复策略:向后恢复,如果任何本地子事务失败,则补偿已完成的事务。例如异常情况的执行顺序为T1T2TiCiC2C1。前向恢复,即重试失败的事务,假设每个子事务最后都会成功。执行顺序:T1,T2,...,Tj(失败),Tj(重试),...,Tn。例如,假设用户下单,花费10元买了10多朵玫瑰,那么:T1=下单T2=扣用户10元T3=给用户加10朵玫瑰T4=库存C1扣10朵玫瑰=取消订单C2=给用户加10元C3=给用户减去10朵玫瑰C4=给库存加10朵玫瑰假设事务执行到T4发生异常回滚,当C4想把玫瑰加回盘点,发现用户的玫瑰花这是Saga的一个缺点,是交易之间缺乏隔离性造成的。这个问题可以通过以下方案解决:在应用层添加逻辑锁逻辑。会话级隔离以确保序列化操作。在业务层面,这部分资金通过预冻结资金进行隔离。更新是通过在业务运行过程中及时读取当前状态来获取的。参考与感谢:干货|一篇文章带你了解分布式事务。如果有人问你分布式事务,就把这篇文章丢给他讲分布式事务,然后讲解决方案MySQL事务实现原理。MySQL事务日志(redolog和undolog)详解《Saga 分布式事务解决⽅案与实践》分布式事务解决方案的BestEffortNotification
