Springcloud——分布式事务

更新时间:2023-06-25 23:49:38 阅读: 评论:0

Springcloud——分布式事务
摘要
主要讲解是分布式事务指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。分布式事务顾名思义就是要在分布式系统中实现事务,它其实是由多个本地事务组合⽽成。对于分布式事务⽽⾔⼏乎满⾜不了ACID,其实对于单机事务⽽⾔⼤部分情况下也没有满⾜ACID,不然怎么会有四种隔离级别呢?所以更别说分布在不同数据库或者不同应⽤上的分布式事务了。
ACID
严格意义上的事务实现应该是具备原⼦性、⼀致性、隔离性和持久性,简称ACID。
原⼦性((Atomicity) ,可以理解为⼀个事务内的所有操作要么都执⾏,要么都不执⾏
⼀致性(Consistency),可以理解为数据是满⾜完整性约束的,也就是不会存在中间状态的数据,⽐如你账上有400,我账上有100,你给我打200块,此时你账上的钱应该是200,我账上的钱应该是300,不会存在我账上钱加了,你账上钱没扣的中间状态。
隔离性(lsolation),指的是多个事务并发执⾏的时候不会互相⼲扰,即⼀个事务内部的数据对于其他事务来说是隔离的。
持久性(Durability),指的是⼀个事务完成了之后数据就被永远保存下来,之后的其他操作或故障都不会对事务的结果产⽣影响。
2PCmueller
2PC(Two-pha commit protocol),中⽂叫⼆阶段提交。⼆阶段提交是⼀种强⼀致性设计,2PC引⼊⼀个事务协调者的⾓⾊来协调管理各参与者(也可称之为各本地资源)的提交和回滚,⼆阶段分别指的
是准备(投票)和提交两个阶段。注意这只是协议或者说是理论指导,只阐述了⼤⽅向,具体落地还是有会有差异的。
两阶段核⼼原理:准备阶段协调者会给各参与者发送准备命令,你可以把准备命令理解成除了提交事务之外啥事都做完了。同步等待所有资源的响应之后就进⼊第⼆阶段即提交阶段(注意提交阶段不⼀定是提交事务,也可能是回滚事务)。假如在第⼀阶段所有参与者都返回准备成功,那么协调者则向所有参与者发送提交事务命令,然后等待所有事务都提交成功之后,返回事务执⾏成功。假如在第⼀阶段有⼀个参与者返回失败,那么协调者就会向所有参与者发送回滚事务的请求,即分布式事务执⾏失败。
ele
那第⼆阶段提交失败的话呢?
这⾥有两种情况。
第⼀种是第⼆阶段执⾏的是回滚事务操作,那么答案是不断重试,直到所有参与者都回滚了,不然那些在第⼀阶段准备成功的参与者会⼀直阻塞着。
第⼆种是第⼆阶段执⾏的是提交事务操作,那么答案也是不断重试,因为有可能⼀些参与者的事务已
经提交成功了,这个时候只有⼀条路,就是头铁往前冲,不断的重试,直到提交成功,到最后真的不⾏只能⼈⼯介⼊处理。
⾸先2PC是⼀个同步阻塞协议,像第⼀阶段协调者会等待所有参与者响应才会进⾏下⼀步操作,当然第⼀阶段的协调者有超时机制,假设因为⽹络原因没有收到某参与者的响应或某参与者挂了,那么超时后就会判断事务失败,向所有参与者发送回滚命令。辅导员工作计划
在第⼆阶段协调者的没法超时,因为按照我们上⾯分析只能不断重试!
协调者故障分析
协调者是⼀个单点,存在单点故障问题。
假设协调者在发送准备命令之前挂了,还⾏等于事务还没开始。
kramer假设协调者在发送准备命令之后挂了,这就不太⾏了,有些参与者等于都执⾏了处于事务资源锁定的状态。不仅事务执⾏不下去,还会因为锁定了⼀些公共资源⽽阻塞系统其它操作。(造成资源的死锁)
假设协调者在发送回滚事务命令之前挂了,那么事务也是执⾏不下去,且在第⼀阶段那些准备成功参与者都阻塞着。
先生的英文缩写假设协调者在发送提交事务命令之后挂了,这个还⾏,也是⾄少命令发出去了,很⼤概率都会提交成功,然后释放资源,但是如果出现⽹络分区问题某些参与者将因为收不到命令⽽阻塞着。(⼀般有超时机制来保证资源的的死锁情况)
协调者故障,通过选举得到新协调者
因为协调者单点问题,因此我们可以通过选举等操作选出⼀个新协调者来顶替。
如果处于第⼀阶段,其实影响不⼤都回滚好了,在第⼀阶段事务肯定还没提交。
如果处于第⼆阶段,假设参与者都没挂,此时新协调者可以向所有参与者确认它们⾃⾝情况来推断下⼀步的操作。
假设有个别参与者挂了!这就有点僵硬了,⽐如协调者发送了回滚命令,此时第⼀个参与者收到了并执⾏,然后协调者和第⼀个参与者都挂了。此时其他参与者都没收到请求,然后新协调者来了,它询问其他参与者都说OK,但它不知道挂了的那个参与者到底O不OK,所以它傻了。
finish是什么意思问题其实就出在每个参与者⾃⾝的状态只有⾃⼰和协调者知道,因此新协调者⽆法通过在场的参与者的状态推断出挂了的参与者是什么情况。虽然协议上没说,不过在实现的时候我们可以灵活的让协调者将⾃⼰发过的请求在哪个地⽅记⼀下,也就是⽇志记录,这样新协调者来的时候不就知道此时该不
该发了?但是就算协调者知道⾃⼰该发提交请求,那么在参与者也⼀起挂了的情况下没⽤,因为你不知道参与者在挂之前有没有提交事务。如果参与者在挂之前事务提交成功,新协调者确定存活着的参与者都没问题,那肯定得向其他参与者发送提交事务命令才能保证数据⼀致。如果参与者在挂之前事务还未提交成功,参与者恢复了之后数据是回滚的,此时协调者必须是向其他参与者发送回滚事务命令才能保持事务的⼀致。所以说极端情况下还是⽆法避免数据不⼀致问题。
2PC是⼀种尽量保证强⼀致性的分布式事务,因此它是同步阻塞的,⽽同步阻塞就导致长久的资源锁定问题,总体⽽⾔效率低,并且存在单点故障问题,在极端条件下存在数据不⼀致的风险。当然具体的实现可以变形,⽽且2PC也有变种,例如Tree 2PC、Dynamic 2PC。还有⼀点不知道你们看出来没,2PC 适⽤于数据库层⾯的分布式事务场景,⽽我们业务需求有时候不仅仅关乎数据库,也有可能是上传⼀张图⽚或者发送⼀条短信。⽽且像Java中的JTA只能解决⼀个应⽤下多数据库的分布式事务问题,跨服务了就不能⽤了。简单说下Java中JTA,它是基于XA规范实现的事务接⼝,这⾥的XA你可以简单理解为基于数据库的XA规范来实现的2PC。
协调者:
write START_2PC to local log; //开始事务
multicast VOTE_REQUEST to all participants; //⼴播通知参与者投票
while not all votes have been collected {
wait for any incoming vote;
if timeout { //协调者超时
write GLOBAL_ABORT to local log; //写⽇志
multicast GLOBAL_ABORT to all participants; //通知事务中断
exit;
}
record vote;
}
fall in love
//如果所有参与者都ok
if all participants nt VOTE_COMMIT and coordinator votes COMMIT {        write GLOBAL_COMMIT to local log;
multicast GLOBAL_COMMIT to all participants;extendexpand
} el {
write GLOBAL_ABORT to local log;
multicast GLOBAL_ABORT to all participants;
}
参与者:
write INIT to local log; //写⽇志
wait for VOTE_REQUEST from coordinator;
if timeout { //等待超时
write VOTE_ABORT to local log;
exit;
oldmacdonald
}
if participant votes COMMIT {
write VOTE_COMMIT to local log; //记录⾃⼰的决策
nd VOTE_COMMIT to coordinator;
wait for DECISION from coordinator;
if timeout {
multicast DECISION_REQUEST to other participants; //超时通知            wait until DECISION is received;  /* remain blocked*/
write DECISION to local log;
}
if DECISION == GLOBAL_COMMIT
write GLOBAL_COMMIT to local log;
el if DECISION == GLOBAL_ABORT
write GLOBAL_ABORT to local log;
} el {
write VOTE_ABORT to local log;
nd VOTE_ABORT to coordinator;
}
每个参与者维护⼀个线程处理其它参与者的DECISION_REQUEST请求:
while true {
wait until any incoming DECISION_REQUEST is received;
read most recently recorded STATE from the local log;
if STATE == GLOBAL_COMMIT
nd GLOBAL_COMMIT to requesting participant;
el if STATE == INIT or STATE == GLOBAL_ABORT;
nd GLOBAL_ABORT to requesting participant;
el
skip;  /* participant remains blocked */
}
3PC
3PC的出现是为了解决2PC的⼀些问题,相⽐于2PC它在参与者中也引⼊了超时机制,并且新增了⼀个阶段使得参与者可以利⽤这⼀个阶段统⼀各⾃的状态。3PC包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英⽂就是:CanCommit、PreCommit和DoCommit 。看起来是把2PC的提交阶段变成了预提交阶段和提交阶段,但是3PC的准备阶段协调者只是询问参与者的⾃⾝状况。不管哪⼀个阶段有参与者返回失败都会宣布事务失败,这和2PC是⼀样的(当然到最后的提交阶段和2PC⼀样只要是提交请求就只能不断重试)。
我们先来看⼀下3PC 的阶段变更有什么影响。
⾸先准备阶段的变更成不会直接执⾏事务,⽽是会先去询问此时的参与者是否有条件接这个事务,因此不会⼀来就⼲活直接锁资源,使得在某些资源不可⽤的情况下所有参与者都阻塞着。⽽预提交阶段的引⼊起到了⼀个统⼀状态的作⽤,它像⼀道栅栏,表明在预提交阶段前所有参与者其实还未都回应,
在预处理阶段表明所有参与者都已经回应了。假如你是⼀位参与者,你知道⾃⼰进⼊了预提交状态那你就可以推断出来其他参与者也都进⼊了预提交状态。但是多引⼊⼀个阶段也多⼀个交互,因此性能会差⼀些,⽽且绝⼤部分的情况下资源应该都是可⽤的,这样等于每次明知可⽤执⾏还得询问⼀次。
我们再来看下参与者超时能带来什么样的影响。
我们知道2PC是同步阻塞的,上⾯我们已经分析了协调者挂在了提交请求还未发出去的时候是最伤的,所有参与者都已经锁定资源并且阻塞等待着。
那么引⼊了超时机制,参与者就不会傻等了,如果是等待提交命令超时,那么参与者就会提交事务了,因为都到了这⼀阶段了⼤概率是提交的,如果是等待预提交命令超时,那该⼲啥就⼲啥了,反正本来啥也没⼲。sore
然⽽超时机制也会带来数据不⼀致的问题,⽐如在等待提交命令时候超时了,参与者默认执⾏的是提交事务操作,但是有可能执⾏的是回滚操作,这样―来数据就不⼀致了。
新协调者来的时候发现有⼀个参与者处于预提交或者提交阶段,那么表明已经经过了所有参与者的确认了,所以此时执⾏的就是提交命令。所以说3PC就是通过引⼊预提交阶段来使得参与者之间的状态得到统⼀,也就是留了⼀个阶段让⼤家同步⼀下。但是这也只能让协调者知道该如果做,但不能保证
这样做⼀定对,这其实和上⾯2PC分析⼀致,因为挂了的参与者到底有没有执⾏事务⽆法断定。所以说3PC通过预提交阶段可以减少故障恢复时候的复杂性,但是不能保证数据⼀致,除⾮挂了的那个参与者恢复。
让我们总结⼀下,3PC相对于2PC做了⼀定的改进:引⼊了参与者超时机制,并且增加了预提交阶段使得故障恢复之后协调者的决策复杂度降低,但整体的交互过程更长了,性能有所下降,并且还是会存在数据不⼀致问题。所以2PC和3PC都不能保证数据100%⼀致,因此⼀般都需要有定时扫描补偿机制。我再说下3PC我没有找到具体的实现,所以我认为3PC只是纯的理论上的东西,⽽且可以看到相⽐于2PC它是做了⼀些努⼒但是效果甚微,所以只做了解即可。
TCC(Seata分布式事务框架)

本文发布于:2023-06-25 23:49:38,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/78/1039755.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:参与者   事务   提交
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图