分布式事务1两阶段提交 二阶段提交协议(TwophaseCommit,即2PC)是常用的分布式事务解决方案,即将事务的提交过程分为两个阶段来进行处理:准备阶段和提交阶段阶段1:准备阶段协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待所有参与者答复。各参与者执行事务操作,将undo和redo信息记入事务日志中(但不提交事务)。如参与者执行成功,给协调者反馈yes,即可以提交;如执行失败,给协调者反馈no,即不可提交阶段2:提交阶段如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(rollback)消息;否则,发送提交(commit)消息。参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源2三阶段提交 三阶段提交协议,是二阶段提交协议的改进版本,与二阶段提交不同的是,引入超时机制。同时在协调者和参与者中都引入超时机制阶段1:canCommit协调者向参与者发送commit请求,参与者如果可以提交就返回yes响应(参与者不执行事务操作),否则返回no响应:协调者向所有参与者发出包含事务内容的canCommit请求,询问是否可以提交事务,并等待所有参与者答复参与者收到canCommit请求后,如果认为可以执行事务操作,则反馈yes并进入预备状态,否则反馈no阶段2:preCommit协调者根据阶段1canCommit参与者的反应情况来决定是否可以进行基于事务的preCommit操作。根据响应情况,有以下两种可能情况1:阶段1所有参与者均反馈yes,参与者预执行事务协调者向所有参与者发出preCommit请求,进入准备阶段参与者收到preCommit请求后,执行事务操作,将undo和redo信息记入事务日志中(但不提交事务)各参与者向协调者反馈ack响应或no响应,并等待最终指令情况2:阶段1任何一个参与者反馈no,或者等待协调者超时,无法收到所有参与者的反馈,即中断事务协调者向所有参与者发出abort请求无论收到协调者发出的abort请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务阶段3:doCommit该阶段进行真正的事务提交,分为以下三种情况情况1:阶段2所有参与者均反馈ack响应,执行真正的事务提交如果协调者处于工作状态,则向所有参与者发出doCommit请求,参与者收到doCommit请求后,会正式执行事务提交,并释放整个事务期间占用的资源各参与者向协调者反馈ack完成的消息,协调者收到所有参与者反馈的ack消息后,即完成事务提交情况2:阶段2任何一个参与者反馈no,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务如果协调者处于工作状态,向所有参与者发出abort请求,参与者使用阶段1中的undo信息执行回滚操作,并释放整个事务期间占用的资源各参与者向协调者反馈ack完成的消息,协调者收到所有参与者反馈的ack消息后,即完成事务中断情况3:协调者与参与者网络出现问题参与者在协调者发出doCommit或abort请求等待超时,仍会继续执行事务提交3TCC Try阶段:需要做资源的检查和预留。在扣钱场景下,Try要做的事情是就是检查账户可用余额是否充足,再冻结账户的资金。Try方法执行之后,账号余额虽然还是100,但是其中30元已经被冻结了,不能被其他事务使用Confirm阶段:扣减Try阶段冻结的资金,Confirm方法执行之后,账号在一阶段中冻结的30元已经被扣除,账号A余额变成70元Cancel阶段:回滚的话,就需要在Cancel方法内释放一阶段Try冻结的30元,使账号的回到初始状态,100元全部可用4saga Saga是一个长活事务,可被分解成可以交错运行的子事务集合,每个子事务有相应的执行模块和补偿模块,当saga事务中的任意一个本地事务出错了,可以通过调用相关事务对应的补偿方法恢复,达到事务的最终一致性。 每个Saga由一系列subtransactionTi组成每个Ti都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果可以看到,和TCC相比,Saga没有预留动作,它的Ti就是直接提交到库。saga不保证acid,只保持服务的基本可用和数据的最终一致性,事务隔离性差,要保证数据不被脏读需要在业务上进行相应的逻辑处理。可以再业务层加入锁隔离相关联的操作Saga的执行顺序有两种:T1,T2,T3,,TnT1,T2,,Tj,Cj,,C2,C1,其中0jnSaga定义了两种恢复策略:backwardrecovery,向后恢复,补偿所有已完成的事务,如果任一子事务失败。即上面提到的第二种执行顺序,其中j是发生错误的subtransaction,这种做法的效果是撤销掉之前所有成功的subtransation,使得整个Saga的执行结果撤销。forwardrecovery,向前恢复,重试失败的事务,假设每个子事务最终都会成功。适用于必须要成功的场景,执行顺序是类似于这样的:T1,T2,,Tj(失败),Tj(重试),,Tn,其中j是发生错误的subtransaction。该情况下不需要Ci。5seata Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案(AT模式是阿里首推的模式)AT模式(阿里分布式框架seata)一阶段:提交 在一阶段,Seata会拦截业务SQL,首先解析SQL语义,找到业务SQL要更新的业务数据,在业务数据被更新前,将其保存成beforeimage,然后执行业务SQL更新业务数据,在业务数据更新之后,再将其保存成afterimage,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性二阶段提交或回滚 二阶段如果是提交的话,因为业务SQL在一阶段已经提交至数据库,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可 二阶段如果是回滚的话,Seata就需要回滚一阶段已经执行的业务SQL,还原业务数据回滚方式便是用beforeimage还原业务数据;但在还原前要首先要校验脏写,对比数据库当前业务数据和afterimage,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理6事务消息本地消息事务表消息队列1在同一个事务中完成订单数据和单独的消息表入库通过cancal将增量消息放入消息队列主动线程轮询查询增量消息放入消息队列,增量可以通过缓存已发送过的消息表最大ID筛选2消费者消费过后,标记事务消息的成功与失败本地消息事务表消息队列kafka事务API。initTransactions和commitTransaction范围需要大于包括数据库的事务操作范围在同一个数据库事务中完成订单数据和单独的消息表入库(处理中)数据库事务提交后,成功再提交kafka事务commitTransaction。失败则abortTransaction事务消息的补偿,为防止队列消息投递失败。定期查询状态是处理中的事务表消息,重新投递消费者消费过后,标记事务消息的成功与失败初始化事务,需要注意确保transation。id属性被分配voidinitTransactions();开启事务voidbeginTransaction()throwsProducerFencedE为Consumer提供的在事务内CommitOffsets的操作voidsendOffsetsToTransaction(MapTopicPartition,OffsetAndMetadataoffsets,StringconsumerGroupId)throwsProducerFencedE提交事务voidcommitTransaction()throwsProducerFencedE放弃事务,类似于回滚事务的操作voidabortTransaction()throwsProducerFencedE复制代码RocketMQ事务消息也是类似kafka,但是更加完善,采用了2PC(两阶段提交)补偿机制(事务状态回查)的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息正常事务消息的发送及提交a、生产者发送half消息到Broker服务端(半消息)半消息是一种特殊的消息类型,该状态的消息暂时不能被Consumer消费b、Broker服务端将消息持久化之后,给生产者响应消息写入结果(ACK响应);c、生产者根据发送结果执行本地事务逻辑(如果写入失败,此时half消息对业务不可见,本地逻辑不执行);d、生产者根据本地事务执行结果向Broker服务端提交二次确认(Commit或是Rollback),Broker服务端收到Commit状态则将半事务消息标记为可投递,订阅方最终将收到该消息;Broker服务端收到Rollback状态则删除半事务消息,订阅方将不会接收该消息事务消息的补偿流程a、在网络闪断或者是应用重启的情况下,可能导致生产者发送的二次确认消息未能到达Broker服务端,经过固定时间后,Broker服务端将会对没有CommitRollback的事务消息(pending状态的消息)进行回查;b、生产者收到回查消息后,检查回查消息对应的本地事务执行的最终结果;c、生产者根据本地事务状态,再次提交二次确认给Broker,然后Broker重新对半事务消息Commit或者Rollback分布式锁1redis的实现方案1:setexpxnx锁可以自动过期加锁和加失效时间两是原子性操作缺点:如果需要回滚删除锁,易出现bug引起误删其他线程加上的锁缺点:锁过期释放了,业务还没执行完,无法延迟过期时间2:setexpxnx。value是当前线程独有的唯一随机值,需要校验value再删除锁可以自动过期加锁和加失效时间是原子性的操作可防止锁被别的线程误删缺点:锁过期释放了,业务还没执行完,无法延迟过期时间3:redissionRedisson可以解决锁过期释放,业务没执行完的问题只要线程一加锁成功,就会启动一个watchdog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了锁过期释放,业务没执行完问题4:Redlockredis如果是单master的,线程A在Redis的master节点上拿到了锁,但是加锁的key还没同步到slave节点。恰好这时,master节点发生故障,一个slave节点就会升级为master节点。线程B就可以获取同个key的锁啦,但线程A也已经拿到锁了,锁的安全性就没了多个Redismaster部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步然后在多台Redismaster同时请求加锁,但加锁redis机器超过一半。并且加锁使用的时间小于锁的有效期,则加锁成功。2数据库的实现方案单点故障:数据库可以多搞个数据库备份没有失效时间:每次加锁时,插入一个期待的有效时间A:定时任务,隔一段时间清理时间失效锁B:下次加锁时则先判断当前时间是否大于锁的有效时间,以此判断锁是否失效不可重入:在数据加锁时加入一个幂等唯一值字段,下次获取时,先判断这个字段是否一致,一致则说明是当前操作重入操作3zookeeperzookeeper临时顺序节点:临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉(可解决分布式锁的自动失效)。另外,在临时节点下面不能创建子节点zookeeper监视器:zookeeper创建一个节点时,会注册一个该节点的监视器,当节点状态发生改变时,watch会被触发,zooKeeper将会向客户端发送一条通知zookeeper分布式锁原理创建临时有序节点,每个线程均能创建节点成功,但是其序号不同,只有序号最小的可以拥有锁,其它线程只需要监听比自己序号小的节点状态即可1:在指定的节点下创建一个锁目录lock2:线程X进来获取锁在lock目录下,并创建临时有序节点3:线程X获取lock目录下所有子节点,并获取比自己小的兄弟节点,如果不存在比自己小的节点,说明当前线程序号最小,顺利获取锁4:此时线程Y进来创建临时节点并获取兄弟节点,判断自己是否为最小序号节点,发现不是,于是设置监听(watch)比自己小的节点(这里是为了发生上面说的羊群效应)5:线程X执行完逻辑,删除自己的节点,线程Y监听到节点有变化,进一步判断自己是已经是最小节点,顺利获取锁 作者:潜行前行 链接:https:juejin。cnpost7128332659490553892 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。