分布式事务Seata
1. 事务简介
1.1 什么是事务
事务是由一组操作构成的可靠的独立的工作单元,这些操作要么全部执行,要么全部不执行。在关系型数据库中,一个事务有一组SQL语句组成。
通俗意义上事务就是为了使得一些更新操作要么都成功,要么都失败。
1.2 事务特性
- 原子性(atomicity):强调事务的不可分割,要么全部成功,要么全部失败
- 一致性(consistency):事务执行前后数据的完整性保持一致
- 隔离性(isolation):一个事务执行过程中不受到其它事务的干扰
- 持久性(durability):事务一旦提交,对数据库中数据的改变就是永久性的
1.3 事务隔离级别
- 脏读: 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
- 不可重复读: 事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
- 幻读: 系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
2. 分布式事务
2.1 简介
本地事务: 当事务由资源管理器本地管理时。大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务
分布式事务: 在分布式系统中,完成某个业务功能可能需要跨多个服务,操作多个数据库,需要操作的多个资源位于多个资源服务器上,而应用需要保证对多个数据库的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性
2.2 典型应用场景
-
垮库事务:一个应用某个功能需要操作多个库,不同的库存储不同的业务数据
-
分库分表:通常一个库数据量比较大或者预期未来数据量比较大,都会进行水平拆分,也就是分库分表
-
微服务:应用拆分成不同的独立服务,独立服务之间通过远程调用进行通信
2.3 解决方案
-
两阶段提交(2PC): 是将事务的提交过程分成提交事务请求和执行提交两个阶段进行处理
阶段一: 协调者向所有参与者发送prepare请求与事务内容,询问是否可以准备事务提交
阶段二: 协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或回滚
提交:
回滚:
优点:尽量保证了数据的强一致
缺点:
单点问题: 事务协调器宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务协调器宕机,资源管理器就会一直阻塞,导致数据库无法使用
同步阻塞: 在准备阶段就绪后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源
数据不一致: 虽然为分布式数据强一致所设计,但仍然存在数据不一致的可能。比如,在第二阶段中,假设事务协调器发出来Commit通知,但因为网络问题,该通知仅被一部分参与者接收到并执行力Commit操作,其余的参与者因为没有收到通知一直处于阻塞状态,这时候就产生了数据不一致性,总的来说,XA协议比较简单,成本较低,但其单点问题,以及不能支持高并发依然是最大的弱点
- 补偿事务(TCC): TCC其实就是采用的补偿机制,其核心思想是,针对每个操作,都要注册一个与其对应的确认和补偿操作。分为三个阶段
Try阶段:对业务系统做检测和资源预留
Confirm阶段:对业务系统做确认提交。Try阶段执行成功并开始执行Confirm阶段时,默认Confirm阶段时不会出错的
Cancel阶段:在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放
优点:跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些
缺点:在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要写很多补偿的代码
-
本地消息表: 核心思想是将分布式事务拆分成本地事务进行处理,这种思路源于ebay
基本思路:消息生产方需要额外建立一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交(即在一个数据库),然后通过MQ将消息发送给消费方,如果发送失败会进行重试。消息消费方需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功,如果失败会重试,如果是业务上的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚。生产方和消费方定时扫描本地消息表,把还没处理完或失败的消息再发送一遍。
优点:避免分布式事务问题,实现了最终一致性
缺点:消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理
-
MQ事务消息: 有一些第三方的MQ是支持事务消息的,比如RocketMQ,类似于两阶段提交
基本思路:第一阶段Prepared消息,会拿到消息的地址;第二阶段执行本地事务;第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvZat3cG-1611990537788)(img\rocketmq事务.png)]
优点:实现了最终一致性,不依赖本地数据库事务
缺点:实现难度大,主流MQ不支持,RocketMQ事务消息部分代码未开源
3. Seata介绍
3.1 Seata简介
Seata是阿里巴巴开源的分布式事务中间件,以高效并对业务0侵入的方式,解决微服务场景下面临的分布式事务问题。
传统2PC的问题在Seata中得到了解决,他通过对本地关系数据库的分支事务的协调来驱动完成全局事务。主要优点是性能较好,且不长时间占用连接资源。
Seata把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分支事务达成一致,要么一起提交,要么一起回滚。
3.2 AT模式(业务入侵小)
Seata AT模式是基于XA事务演进而来的一个分布式事务中间件,XA是一个基于数据库实现的分布式事务协议,本质上和两阶段提交一样,需要数据库支持,Mysql5.6以上版本支持XA协议,其他数据库如Oracle,DB2也实现了XA接口
3.2.1 核心组件
- Transaction Coordinator(TC): 事务协调器,它是独立的中间件,需要独立部署运行,它维护全局事务的运行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信协调各个分支事务的提交和回滚。
- Transaction Manager(TM): 事务管理器,TM需要嵌入应用程序中工作,他负责开启一个全局事务,并最终向TC发起全局提交或全局回滚的指令。
- Recourse Manager(RM): 控制分支事务,负责分支注册、状态汇报、并接受TC的指令,驱动分支事务的提交和回滚。
3.2.2 两阶段
-
第一阶段
Seata的JDBC数据源代理通过对业务SQL的解析,把业务数据在更新前后的数据镜像组织成回滚日志,利用本地事务的ACID特性,将业务数据的更新和回滚日志写在同一个本地事务中提交。这样可以保证,任何提交的业务数据的更新一定有相应的回滚日志存在
一阶段提交后,立马释放本地事务锁定的资源。
-
第二阶段
如果决议是全局提交,此时分支事务已经完成提交,只需要异步清理回滚日志。
如果决议是全局回滚,RM收到TC发来的回滚请求,通过XID和BranchID找到相应的回滚日志记录,进行回滚。
3.3 执行流程
- TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
- XID在微服务调用链路的上下文中传播
- RM向TC注册分支事务,将其纳入XID对应全局事务的管辖,并会执行分支事务并提交,并将执行结果汇报给TC
- TM根据TC中的分支事务执行情况,向TC发起针对XID的全局提交或回滚协议
- TC调度XID下管辖的全部分支事务完成提交或回滚请求
3.4 Seata使用
https://blog.youkuaiyun.com/weixin_43848040/article/details/113422082