分布式事务解决方案简述

分布式事务解决方案简述,第1张

分布式事务解决方案简述

分布式事务常用解决思路有2PC、3PC、TCC、saga、消息等

目录

2PC

2PC参考实现

3PC

TCC

TCC参考实现

SAGA

实现saga事务的两种思路

消息事务

2PC

两阶段提交,引入一个第三方协调者来管理整个分布式事务,各系统本地事务为参与者。将分布式事务分为两个阶段,一阶段Prepare,二阶段Commit/Rollback。
一阶段执行各参与者的本地事务,执行完后向协调者反馈执行成功/失败情况,但事务不提交。
协调者判断若所有反馈都是成功,会进入二阶段向所有参与者发送commit命令,各参与者提交本地事务。若有参与者反馈失败或协调者方超时未得到反馈,协调者则向所有参与者发送rollback命令,各参与者回滚本地事务。
存在缺点:
1、协调者单点故障,一旦协调者下线,各本地事务资源便长时间无法释放。可做高可用解决、3PC
2、参与者与协调者交互中资源(DB连接)占用时间长,性能降低。优化可参考seata-at,保存数据更新前后镜像,随即释放连接。
3、二阶段提交或回滚时,部分参与者因网络故障或各种原因未接收到commit/rollback命令,导致数据不一致。解决:人工干预

2PC参考实现

seata框架 AT模式 原理:
通过代理DataSource数据源,在各本地事务执行DML语句时,解析sql,查询被 *** 作的数据行在更新前和更新后的数据快照,将快照保存到日志表中,随本地事务一起提交。
例如执行语句update tablea set a=1,b=1 where c = ‘das’时会进行如下 *** 作
1)解析update语句,构造查询SQL: select a, b, pk from tablea where c=’das’; 先查询执行前数据行数据, pk为主键
2)执行update语句
3)再次执行select a, b from tablea where pk = ‘主键值’; 查询执行后数据行数据
4)将执行前后的数据行数据快照保存到undo日志表中
这样就解决了2PC缺点中的第二条,假如此分布式事务需要回滚,则从undo日志表中查询出快照数据,根据执行前数据行重新构造update table set…语句恢复数据。
全局锁:
以上的 *** 作存在一个问题,各参与者保存数据快照后本地事务提交,但是分布式事务还未结束,那么很有可能存在数据脏读、覆盖的可能,因为本地事务提交后,其他事务若修改了该条数据,此时分布式事务回滚,undo日志表中的数据则会覆盖其他事务的修改。
seata对此也进行了处理,在逻辑层对数据行进行加锁,在当前分布式事务结束释放锁前,其他分布式事务无法修改该行数据。本质就是有一张lock表,表中存分布式事务ID,被锁的表名,被锁的主键名,在执行本地DML前,查询lock表中是否存在数据,不存在则表示未锁,那么插入一条数据表示当前事务对该行数据加锁。

3PC

2PC模型两个阶段,只有协调者存在超时机制,协调者长时间未接收到参与者反馈则认为事务失败,通知所有参与者回滚。若此时协调者下线,未接收到commit/rollback命令的参与者资源仍旧锁定等待,无法继续完成事务。而且2PC模型参与者理论上也无法增加超时机制,因为参与者无法得知其他参与者状态,无法判断自己该提交还是该回滚。
因此为了解决这个问题,3PC将2PC第二阶段又拆分了一个新阶段:一阶段Prepare,二阶段PreCommit,三阶段doCommit/doRollback。一阶段执行事务和2PC相同,二阶段询问各个参与者是否能够提交,当所有参与者状态都达成一致后能够提交之后,协调者发起第三阶段doCommit命令。

参与者超时 *** 作:
1、参与者在第一阶段因协调者下线或网络问题,长时间没有接收到协调者发来的二阶段指令,参与者等待超时后中断事务
2、参与者在第二阶段因协调者下线或网络问题,长时间没有接收到协调者发来的三阶段指令,参与者等待超时后提交事务。因为当前参与者已经进入了第二阶段,意味着所有参与者都已经Prepare执行成功了,所以虽然没有接收到具体的第三阶段指令,但它有理由认为事务很有可能成功,所以提交

3PC相比2PC解决了协调者宕机之后的事务阻塞问题,但是并不完全解决数据一致性,因为假如参与者当前在第二阶段,因网络原因没有接收到协调者发来的第三阶段doRollback,自个提交了,还是会导致数据不一致。

参考实现:网上普遍说没有

TCC

Try//confirm/i/Cancel,业务层面上的2PC思想,将一个业务 *** 作分为三部分
Try 执行资源的预留和锁定。
Confirm 使用try中预留的资源执行真正的业务 *** 作,不做校验。
Cancel 撤销try阶段预留的资源
由主业务活动发起分布式事务,并向协调者注册,每个参与者提供TCC式业务 *** 作(三个接口),主业务依次执行各个参与者的try方法并将执行结果告知协调者,在业务活动结束准备提交时,由协调者判断参与者try *** 作是否全部执行成功,若成功则调用各参与者的/confirm/i接口,若存在失败,调用各参与者的cancel接口。
例如发送短信接口,将分为三个接口:保存短信草稿,发送短信,删除短信草稿

TCC参考实现

tcc-transaction框架
原理不复杂,在主业务入口使用AOP创建事务、开启事务,事务对象Transaction中有参与者列表的成员变量List, 程序走到支持TCC式调用的方法时,框架会将该方法和它对应的/confirm/i/cancel方法、入参等封装为一个参与者Participant,添加到当前事务Transaction中,在执行完业务 *** 作事务提交时, 事务对象Transaction首先循环List,执行参与者Participant的/confirm/i方法,之后更新事务状态为已提交;若报错执行事务回滚,同样,循环列表执行Participant的cancel方法,之后更新事务状态为已回滚。

SAGA

将一个长事务分解为多个本地事务Ti,每个本地事务Ti都存在其对应的补偿动作Ci,用于撤销Ti执行后造成的影响。与TCC相比,没有try动作,Ti就是直接提交到库中。那么saga事务就有两种执行方式:
1、T1、T2、T3、…Tn ,向前恢复。认为所有子事务最终都会成功,如果中途失败,不断重试执行,这种情况则不需要补偿动作Ci
2、T1、T2、T3…Tj、Cj…C3、C2、C1 其中0 < j < n,向后恢复。任一事务失败,则补偿所有已完成的事务,使得整个saga事务执行结果撤销。

实现saga事务的两种思路

1、 基于状态机引擎,使用BPMN流程图来定义各个业务 *** 作的执行顺序、对事务 *** 作进行编排,状态机引擎来驱动整个流程图的运行,当出现异常时状态机引擎根据流程图定义执行相应的补偿节点将事务回滚。
正向业务是否补偿也能自定义,可以根据业务特点选择优先重试还是优先回滚,也就是向前恢复和向后恢复都支持,也容易实现节点并发执行、异步执行等 *** 作。
实现参考:工作流框架、seata-saga模式、简单实现就是for循环+执行日志表

2、 基于拦截器的实现。在需要执行的方法T上通过注解或其他形式定义它对应的补偿方法C,在saga事务开启后,通过拦截器拦截每个正向方法T并记录相关信息,当出现异常时触发回滚 *** 作,调用正向方法的补偿方法C。
基于代理的拦截器实现,实现和开发简单,对现有业务改造小,但向前恢复模式难以实现。这种方式实现方式和TCC模式异曲同工,我认为就是/confirm/i *** 作为空的TCC。

消息事务

异步调用
本地消息表:一张存放消息的表,将执行业务和保存消息表的 *** 作放在一个事务中,这样保证消息和本地业务数据是一致的。然后后台定时任务去查询消息表,根据消息内容调用相应的服务,调用成功则更新消息表状态,调用失败,更新消息状态为失败,再另有重试机制重新调用。

消息中间件事务:调用方先向消息中间件发送半消息,半消息指的是暂时对消费者不可见的消息,调用方再执行本地事务,执行完毕后再向中间件发送Commit命令,之后消费者才能消费该消息。如果本地事务执行失败,向中间件发送Rollback命令,删除该消息。如果因网络不通或其他原因,中间件长时间未接收到commit/rollback命令,中间件会主动调用调用方接口查询该次事务的执行状态,然后执行对应的commit/rollback *** 作,需要调用方提供反查询接口。
 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5685063.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存