当前位置: 首页 > 科技观察

分布式事务:分布式事务和Seata核心原理介绍_0

时间:2023-03-16 18:50:50 科技观察

今天,我们将正式进入分布式事务篇章。首先简单介绍一下分布式事务的核心原理和SpringCloudAlibaba技术栈中的Seata框架。本章概述了分布式事务。分布式事务是互联网行业一直绕不开的技术难题。如何更高效地学习分布式事务?Seata从Seata官网介绍Seata相关内容。链接:https://seata.io/zh-cn/docs/overview/what-is-seata.html什么是Seata?Seata是一个开源的分布式事务解决方案,致力于提供高性能易用的分布式事务服务。Seata将为用户提供AT、TCC、SAGA和XA交易模式,为用户打造一站式的分布式解决方案。AT模式前提是基于支持本机ACID事务的关系数据库。Java应用程序,通过JDBC访问数据库。整体机制演化两阶段提交协议:第一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。阶段2:提交是异步的并且完成得非常快。回滚通过单阶段回滚日志进行反向补偿。第一阶段写隔离在提交本地事务之前,需要保证先获得“全局锁”。如果得不到“全局锁”,就无法提交本地事务。试图拿“全局锁”限制在一定范围内。如果超出范围,则放弃并回滚本地事务,释放本地锁。举个例子说明:两个全局事务tx1和tx2分别更新表a的m字段,m的初始值为1000。tx1先启动,启动本地事务,获取本地锁,更新m=1000-100=900,本地事务提交前,先获取记录的“全局锁”,本地提交释放本地锁。启动tx2后,启动本地事务,获取本地锁,更新操作m=900-100=800。在本地事务提交之前,尝试获取记录的“全局锁”。在tx1被全局提交之前,记录的全局锁由tx1持有,tx2需要重试等待“全局锁”。tx1两阶段全局提交,释放“全局锁”。tx2获得“全局锁”提交本地事务。如果是tx1的两阶段全局回滚,那么tx1需要重新获取数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。此时如果tx2持有本地锁,还在等待数据的“全局锁”,那么tx1的分支回滚就会失败。分支的回滚会一直重试,直到tx2的“全局锁”等锁超时,放弃“全局锁”回滚本地事务释放本地锁,tx1的分支回滚终于成功了。因为整个进程的“全局锁”一直由tx1持有,直到tx1结束,所以不会出现“脏写”问题。读隔离基于数据库本地事务隔离级别“已提交读(ReadCommitted)”或以上,Seata(AT模式)默认的全局隔离级别是“未提交读(ReadUncommitted)”。如果应用在特定场景下,需要全局“readcommitted”。目前的Seata方法是通过SELECTFORUPDATE语句的代理。SELECTFORUPDATE语句的执行会申请一个“全局锁”。如果“全局锁”被其他事务持有,则释放本地锁(回滚本地执行的SELECTFORUPDATE语句)并重试。在这个过程中,查询一直处于阻塞状态,直到获得“全局锁”,即读到的相关数据被“提交”,才不会返回。出于整体性能的考虑,Seata目前的方案并没有代理所有的SELECT语句,只代理FORUPDATESELECT语句。工作机制用一个例子来说明整个AT分支的工作过程。业务表:productFieldTypeKeyidbigint(20)PRInamevarchar(100)sincevarchar(100)AT分支事务业务逻辑:updateproductsetname='GTS'wherename='TXC';“一步”过程:解析SQL:获取SQL的类型(UPDATE)、表(product)、条件(wherename='TXC')等相关信息。查询前镜像:根据分析得到的条件信息生成查询语句,定位数据。选择id,名称,因为来自名称='TXC'的产品;获取上图:idnamesince1TXC2014执行业务SQL:将这条记录的名称更新为'GTS'。后查询镜像:根据前镜像的结果,通过“主键”定位数据。选择id,名称,因为来自id=1的产品;获取后镜像:idnamesince1GTS2014插入回滚日志:将前后镜像数据和业务SQL相关信息组成回滚日志记录,插入到UNDO_LOG表中。{"branchId":641789253,"undoItems":[{"afterImage":{"rows":[{"fields":[{"name":"id","type":4,"value":1},{"name":"name","type":12,"value":"GTS"},{"name":"since","type":12,"value":"2014"}]}],"tableName":"product"},"beforeImage":{"rows":[{"fields":[{"name":"id","type":4,"value":1},{"name":"name","type":12,"value":"TXC"},{"name":"since","type":12,"value":"2014"}]}],"tableName":"product"},"sqlType":"UPDATE"}],"xid":"xid:xxx"}提交前向TC注册分支:申请主键值等于1的记录在产品表“全局锁定”中。本地事务提交:业务数据的更新与前面步骤生成的UNDOLOG一起提交。将本地事务提交结果报告给TC。“Phase2-Rollback”接收到TC的分支回滚请求,启动本地事务,进行以下操作。通过XID和BranchID找到对应的UNDOLOG记录。数据校验:将UNDOLOG中的后镜像与当前数据进行对比。如果有差异,则意味着数据已被当前全局事务以外的操作修改。这种情况下,需要根据配置策略进行处理,详细说明另文介绍。根据UNDOLOG中的前像和业务SQL信息生成并执行回滚语句:updateproductsetname='TXC'whereid=1;提交本地交易。并将本地事务的执行结果(即分支事务回滚的结果)报告给TC。“Phase2-Submission”接收到TC的分支提交请求,将请求放入异步任务队列,立即将提交成功的结果返回给TC。异步任务阶段的分支提交请求,会异步批量删除对应的UNDOLOG记录。附录“回滚日志表”UNDO_LOG表:不同的数据库类型略有不同。以MySQL为例:FieldTypebranch_idbigintPKxidvarchar(100)contextvarchar(128)rollback_infolongbloblog_statustinyintlog_createddatetimelog_modifiedatetime--这里注意0.7.0+添加字段contextCREATETABLE`undo_log`(`id`bigint(20)NOTNULLAUTO_INCREMENT(20`bigint)NOTNULL,`xid`varchar(100)NOTNULL,`context`varchar(128)NOTNULL,`rollback_info`longblobNOTNULL,`log_status`int(11)NOTNULL,`log_created`datetimeNOTNULL,`log_modified`datetimeNOTNULL,PRIMARYKEY(`id`),UNIQUEKEY`ux_undo_log`(`xid`,`branch_id`))ENGINE=InnoDBAUTO_INCREMENT=1DEFAULTCHARSET=utf8;TCCSchemaReviewOverview:ADistributed全局事务,整体上是“两阶段提交”模型。全局事务由多个分支事务组成,分支事务必须满足“两阶段提交”的模型要求,即每个分支事务需要有自己的:one-stagepreparebehavior,two-phasecommit或者回滚行为根据两阶段行为模式的不同,我们将分支事务分为“自动(Branch)事务模式”和“手动(Branch)事务模式”。AT模式(参考链接TBD)是基于一个“支持本地ACID事务”的“关系型数据库”:one-stageprepare行为:在一个本地事务中,业务数据更新和相应的回滚日志记录一起提交。两阶段提交行为:立即成功完成,并“自动”异步批量清理回滚日志。两阶段回滚行为:通过回滚日志,“自动”产生补偿操作完成数据回滚。相应的,TCC模式不依赖底层数据资源的事务支持:单阶段准备行为:调用“自定义”准备逻辑。两阶段提交行为:调用“自定义”提交逻辑。两阶段回滚行为:调用“自定义”回滚逻辑。所谓TCC模式,是指支持将“自定义”分支事务纳入全局事务管理。Saga模式Saga模式是SEATA提供的一种长事务解决方案。在Saga模式下,业务流程中的每个参与者都提交一个本地事务。当参与者失败时,先前成功的参与者将得到补偿。第一阶段转发服务和第二阶段补偿服务都是由业务开发来实现的。理论基础:Hector&Kenneth发表了论文Sagas(1987)。适用场景业务流程长,业务流程多。参与者包括无法提供TCC模式所需的三个接口的其他公司或遗留系统服务。优点一阶段提交本地事务,无锁,高性能。事件驱动架构,参与者可以异步执行,吞吐量高。补偿服务易于实施。缺点是不能保证隔离。