阿里开源的分布式框架Seata,这么火你竟然还不知道!

# 前言 # **1. Seata 概述** Seata 是 Simple Extensible Autonomous Transaction Architecture 的简写,由 feascar 改名而来。 Seata 是阿里开源的分布式事务框架,属于二阶段提交模式。 目前github上已经有 12267 颗星了,也很活跃,最新的提交时间很多都是几天前。 首先我们回顾一下在单体应用中,例如一个业务调用了3个模块,他们都使用同一个数据源,是靠本地事务来保证事务一致性。 分布式面试真题地址:[金三银四面试题总结合集](https://mp.weixin.qq.com/s/VZ7wormwowlSPnA_XAnZaw) ![](https://upload-images.jianshu.io/upload_images/24630328-de3815cac15de359.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 但在微服务架构中,这3个模块会变为3个独立的微服务,各自有自己的数据源,调用逻辑就变为: ![](https://upload-images.jianshu.io/upload_images/24630328-4be9c4e9e56f8d04.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) **Seata 如何处理呢?** ![](https://upload-images.jianshu.io/upload_images/24630328-e444c7031f92dbb1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) Business 是业务入口,在程序中会通过**注解**来说明他是一个**全局事务**,这时他的角色为 TM(事务管理者)。 Business 会请求 TC(事务协调器,一个独立运行的服务),说明自己要开启一个全局事务,TC 会生成一个全局事务ID(XID),并返回给 Business。 Business 得到 XID 后,开始调用微服务,例如调用 Storage。 ![](https://upload-images.jianshu.io/upload_images/24630328-425c40bdd3878081.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) (和上面的图一样,方便查看,防止滚到到这儿时已经看不到上面的图片了) Storage 会收到 XID,知道自己的事务属于这个全局事务。Storage 执行自己的业务逻辑,操作本地数据库。 Storage 会把自己的事务注册到 TC,作为这个 XID 下面的一个**分支事务**,并且把自己的事务执行结果也告诉 TC。 此时 Storage 的角色是 RM(资源管理者),资源是指本地数据库。 Order、Account 的执行逻辑与 Storage 一致。 在各个微服务都执行完成后,TC 可以知道 XID 下各个分支事务的执行结果,TM(Business) 也就知道了。 Business 如果发现各个微服务的本地事务都执行成功了,就请求 TC 对这个 XID 提交,否则回滚。 TC 收到请求后,向 XID 下的所有分支事务发起相应请求。 各个微服务收到 TC 的请求后,执行相应指令,并把执行结果上报 TC。 **重要机制** (1)全局事务的回滚是如何实现的呢? Seata 有一个重要的机制:**回滚日志**。 每个分支事务对应的数据库中都需要有一个回滚日志表 UNDO\_LOG,在真正修改数据库记录之前,都会先记录修改前的记录值,以便之后回滚。 在收到回滚请求后,就会根据 UNDO\_LOG 生成回滚操作的 SQL 语句来执行。 如果收到的是提交请求,就把 UNDO\_LOG 中的相应记录删除掉。 (2)RM 是怎么自动和 TC 交互的? 是通过**监控拦截JDBC**实现的,例如监控到开启本地事务了,就会自动向 TC 注册、生成回滚日志、向 TC 汇报执行结果。 (3)二阶段回滚失败怎么办? 例如 TC 命令各个 RM 回滚的时候,有一个微服务挂掉了,那么所有正常的微服务也都不会执行回滚,当这个微服务重新正常运行后,TC 会重新执行全局回滚。 **1.3 核心组件** 回顾一下其中的**核心组件**: - 事务协调器 TC 维护全局和分支事务的状态,指示全局提交或者回滚。 - 事务管理者 TM 开启、提交或者回滚一个全局事务。 - 资源管理者 RM 管理执行分支事务的那些资源,向TC注册分支事务、上报分支事务状态、控制分支事务的提交或者回滚。 **1.4 具体工作过程** 再从宏观上梳理一下 Seata 的工作过程: ![](https://upload-images.jianshu.io/upload_images/24630328-6b20ee0a6c84dfbf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - TM 请求 TC,开始一个新的全局事务,TC 会为这个全局事务生成一个 XID。 - XID 通过微服务的调用链传递到其他微服务。 - RM 把本地事务作为这个XID的分支事务注册到TC。 - TM 请求 TC 对这个 XID 进行提交或回滚。 - TC 指挥这个 XID 下面的所有分支事务进行提交、回滚。 # **2. Seata 详细工作流程示例** 下面我们通过一个分支事务的执行过程来了解 Seata 的工作流程。 例如有一个业务表 product(id,name),分支事务的业务逻辑: ```javascript update product set name = 'GTS' where name = 'TXC'; ``` **2.1 一阶段** (1)解析 SQL 得到 SQL 的类型(UPDATE),表(product),条件(where name = 'TXC')等相关的信息。 (2)查询前镜像 根据解析得到的条件信息,生成查询语句,定位数据。 ```javascript select id, name from product where name = 'TXC'; ``` 得到前镜像: ![](https://upload-images.jianshu.io/upload_images/24630328-c9c773ba9b565762.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) (3)执行业务 SQL 执行自己的业务逻辑: ```javascript update product set name = 'GTS' where name = 'TXC'; ``` 把 name 改为了 GTS。 (4)查询后镜像 根据前镜像的结果,通过 主键 定位数据。 ```javascript select id, name from product where id = 1; ``` 得到后镜像: ![](https://upload-images.jianshu.io/upload_images/24630328-3dce0bc702923eb5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) (5)插入回滚日志 把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO\_LOG 表中。 (6)提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁 。 (7)本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。 (8)将本地事务提交的结果上报给 TC。 **2.2 二阶段 - 回滚** (1)收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。 (2)通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。 (3)数据校验 拿 UNDO LOG 中的后镜与当前数据进行比较,根据校验结果决定是否做回滚。 (4)根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句: ```javascript update product set name = 'TXC' where id = 1; ``` (5)提交本地事务 并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。 **二阶段 - 提交** (1)收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。 (2)异步任务阶段的分支提交请求,将异步和批量地删除相应 UNDO LOG 记录。 # **3. 小结** 上面介绍的是 Seata 的 AT 模式,就是自动化事务,使用非常简单,对业务代码没有侵入性。 不足的地方是目前文档比较少,网上的相关材料也不是很多,所以使用过程中遇到问题时可能就需要自己查看源码,分析原理。 Seata 还支持 TCC 和 Saga 模式,但支持的主要方式是 AT,更多学习笔记+思维导图+面试资料可以关注公众号:麒麟改bug获取!
上一篇:MySQL事务--两阶段提交


下一篇:MySQL上机第一章,创建S,C,T,SC,TC表