首页 > 编程知识 正文

分布式事务seate,seata使用案例

时间:2023-05-05 23:03:23 阅读:285343 作者:292

本地事务执行流程流程

数据库undo.log的工作原理
在操作数据之前会先将数据备份到Undo.log中,然后进行修改数据,如果出现错误或者用户执行
ROLLBACK语句,系统可以利用Undo.log中的备份将数据恢复到事务开始之前的状态。

执行流程
假设有A、B两个数据,值为 1,2
1、事务开始
2、记录A=1到undo.log
3、修改A=3
4、记录B=2到undo.log
5、修改B=4
6、将undo.log写入磁盘
7、将数据写入到磁盘
8、事务提交

在事务提交之前事务备份和修改先在内存中执行,效率高,然后写入磁盘持久化

事务如何保证持久化呢
先写入磁盘持久化,再提交事务,也就是说提交事务之前已一定会写入磁盘

数据库Redo.log(和undo.log相反)undo.log是记录旧数据,redo.log是记录新数据
假设有A、B两个数据,值为 1,2
1、事务开始
2、记录A=1到undo.log buffer(内存缓冲区)
3、修改A=3
4、记录A=3到redo.log buffer
5、记录B=2到undo.log buffer
6、修改B=4
7、记录B=4到redo.log buffer
8、将undo.log写入磁盘
9、将redo.log写入磁盘
10、事务提交

undo.log是写入到redo.log中的 所以说先记录旧数据在记录新数据
undo.log主要是记录事务前的数据(不可替代)

磁盘写入数据方式,简图

磁盘写入方式分为: 随机写和顺序写
redo.log的顺序写(效率高)
数据库数据写入磁盘是随机写(需要先寻址(耗费大量时间))

什么是CAP

Consistency(一致性)

Availability(可用性)

Partition tolerance (分区容错性)

1、先看 Partition tolerance,中文叫做"分区容错"。

大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在上海,另一台服务器放在北京,这就是两个区,它们之间可能因网络问题无法通信。

         上图中,G1 和 G2 是两台跨区的服务器。G1 向 G2 发送一条消息,G2 可能无法收到。系统设计的时候,必须考虑到这种情况。
       一般来说,分布式系统,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。根据CAP 定理,剩下的 C 和 A 无法同时做到。

2.Consistency

        写操作之后的读操作,必须返回该值。举例来说,某条记录是 v0,用户向 G1 发起一个写操作,将其改为 v1。

 接下来,用户的读操作就会得到 v1。这就叫一致性。

 问题是,用户有可能向 G2 发起读操作,由于 G2 的值没有发生变化,因此返回的是 v0。G1 和 G2 读操作的结果不一致,这就不满足一致性了。

 为了让 G2 也能变为 v1,就要在 G1 写操作的时候,让 G1 向 G2 发送一条消息,要求 G2 也改成 v1。

 这样的话,用户向 G2 发起读操作,也能得到 v1。

 3.Availability

        Availability 中文叫做"可用性",意思是只要收到用户的请求,服务器就必须给出回应(对和错不论)。
        用户可以选择向 G1 或 G2 发起读操作。不管是哪台服务器,只要收到请求,就必须告诉用户,到底是 v0 还是 v1,否则就不满足可用性。

4.Consistency 和 Availability 的矛盾

        一致性和可用性,为什么不可能同时成立?
        答案很简单,因为可能通信失败(即出现分区容错)。
        如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,没有可用性不。
        如果保证 G2 的可用性,那么势必不能锁定 G2,所以一致性不成立。
        综上所述,G2 无法同时做到一致性和可用性。系统设计时只能选择一个目标。如果追求一致性,那么无法保证所有节点的可用性;如果追求所有节点的可用性,那就没法做到一致性。

Base理论

BASE是三个单词的缩写:

Basically Available(基本可用)

Soft state(软状态)

Eventually consistent(最终一致性)(G1-G2数据同步需要隔一段时间(比如:1毫秒))

一致性不是立刻一 致性,是隔一段时间后最终一直

基本可用是基本可以使用(不是完全不可用)隔一段时间后就可以使用了。

由上面的两种思想,延伸出了很多的分布式事务解决方案:

XA

TCC

可靠消息最终一致

AT

TCC

它本质是一种补偿的思路。事务运行过程包括三个方法,

Try:资源的检测和预留;

Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;

Cancel:预留资源释放。

执行分两个阶段:

准备阶段(try):资源的检测和预留;

执行阶段(confirm/cancel):根据上一步结果,判断下面的执行方法。如果上一步中所有事务参与者都成功,则这里执行confirm。反之,执行cancel

优势

TCC执行的每一个阶段都会提交本地事务并释放锁,并不需要等待其它事务的执行结果。而如果其它事务执行失败,最后不是回滚,而是执行补偿操作。这样就避免了资源的长期锁定和阻塞等待,执行效率比较高,属于性能比较好的分布式事务方式。

缺点

代码侵入:需要人为编写代码实现try、confirm、cancel,代码侵入较多

开发成本高:一个业务需要拆分成3个步骤,分别编写业务实现,业务编写比较复杂

安全性考虑:cancel动作如果执行失败,资源就无法释放,需要引入重试机制,而重试可能导致重复执行,还要考虑重试时的幂等问题

AT模式

        2019年 1 月份,Seata 开源了 AT 模式。AT 模式是一种无侵入的分布式事务解决方案。可以看做是对TCC或者二阶段提交模型的一种优化,解决了TCC模式中的代码侵入、编码复杂等问题。

在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

可以参考Seata的官方文档。

2.7.1.基本原理

先来看一张流程图:

有没有感觉跟TCC的执行很像,都是分两个阶段:

一阶段:执行本地事务,并返回执行结果

二阶段:根据一阶段的结果,判断二阶段做法:提交或回滚

但AT模式底层做的事情可完全不同,而且第二阶段根本不需要我们编写,全部有Seata自己实现了。也就是说:我们写的代码与本地事务时代码一样,无需手动处理分布式事务。

那么,AT模式如何实现无代码侵入,如何帮我们自动实现二阶段代码的呢?

阶段一流程

 阶段二执行流程

提交操作:

        删除undo.log和redo.log等日志

回滚操作:

        查询undo.log,通过undo.log进行回滚

        回滚前 那redo.log 和数据库数据进行对比,看数据是否一致,如果一致直接回滚,不一致说明有其他人对数据进行了修改,此时需要人工干预,出现了脏数据。

这里的before image和after image类似于数据库的undo和redo日志,但其实是用数据库模拟的。

一阶段

        在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后获取全局行锁,提交事务。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

二阶段提交

二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。

二阶段回滚:

二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。

不过因为有全局锁机制,所以可以降低出现脏写的概率。

AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务 SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。

2.7.2.详细架构和流程

Seata中的几个基本概念:

TC(Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚(TM之间的协调者)。

TM(Transaction Manager) - 事务管理器

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM(Resource Manager) - 资源管理器

管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

我们看下面的一个架构图

TM:业务模块中全局事务的开启者

向TC开启一个全局事务

调用其它微服务

RM:业务模块执行者中,包含RM部分,负责向TC汇报事务执行状态

执行本地事务

向TC注册分支事务,并提交本地事务执行结果

TM:结束对微服务的调用,通知TC,全局事务执行完毕,事务一阶段结束

TC:汇总各个分支事务执行结果,决定分布式事务是提交还是回滚;

TC 通知所有 RM 提交/回滚 资源,事务二阶段结束。

一阶段:

TM开启全局事务,并向TC声明全局事务,包括全局事务XID信息

TM所在服务调用其它微服务

微服务,主要有RM来执行

查询before_image

执行本地事务

查询after_image

生成undo_log并写入数据库

向TC注册分支事务,告知事务执行结果

获取全局锁(阻止其它全局事务并发修改当前数据)

释放本地锁(不影响其它业务对数据的操作)

待所有业务执行完毕,事务发起者(TM)会尝试向TC提交全局事务

二阶段:

TC统计分支事务执行情况,根据结果判断下一步行为

分支都成功:通知分支事务,提交事务

有分支执行失败:通知执行成功的分支事务,回滚数据

分支事务的RM

提交事务:直接清空before_image和after_image信息,释放全局锁

回滚事务:

校验after_image,判断是否有脏写

如果没有脏写,回滚数据到before_image,清除before_image和after_image

如果有脏写,请求人工介入

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。