Spring Cloud Alibaba Seata 分布式事务解决方案简介

本文目录:

    • 写在开头
    • 1.分布式事务的问题
      • 1.1 用例
      • 1.2 架构图
      • 1.3 分布式事务解决方案
    • 2 什么是 Seata
    • 3.Seata 术语表
    • 4. Seata 功能
    • 5. AT 模式
        • Ⅰ.前提
        • Ⅱ.优缺点
        • Ⅲ.整体机制
        • Ⅳ.写隔离
        • Ⅴ.读隔离
        • Ⅵ.工作机制
          • 一阶段
          • 二阶段-回滚
          • 二阶段-提交


写在开头

  接上一篇文章:Sentinel 控制台规则持久化。从本节内容开始,我们来了解一个全新的产物:Spring Cloud Alibaba Seata 分布式事务。开篇先附上部分 Seata 相关内容:

  1. Seata 官网
  2. Seata 源码 GitHub 地址
  3. Seata 官方文档(比较鸡肋,介绍不清楚)
  4. Seata 下载地址

1.分布式事务的问题

  学习到Seata,我默认大家对事务已经有了一定的了解,关于事务此处就不过多介绍。在之前 单机单库 环境下,针对事务的处理还是比较简单的。尤其是结合 Spring 框架,可以说是一个@Transaction 注解走天下。事务 & Spring 事务相关内容,点击链接去了解吧:事务 & Spring 事务内容介绍

  在如今 Spring Cloud 分布式微服务架构体系中,按业务模块划分,一个模块使用一个数据库。多个模块配合来完成一个业务,我们就从 官网 的一个微服务实例开始吧。

1.1 用例

用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:

  • 仓储服务:对给定的商品扣除仓储数量。
  • 订单服务:根据采购需求创建订单。
  • 帐户服务:从用户帐户中扣除余额。

1.2 架构图

Spring Cloud Alibaba Seata 分布式事务解决方案简介_第1张图片

1.3 分布式事务解决方案

  单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证,但是全局数据一致性问题是无法保证的。救世主 Seata 它来了

2 什么是 Seata

  Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。 Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

  在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一致性中间件的角色,帮助经济体平稳的度过历年的双11,对各部门业务进行了有力的支撑。经过多年沉淀与积累,商业化产品先后在阿里云、金融云进行售卖。2019.1 为了打造更加完善的技术生态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助其技术更加可靠与完备。

此处重点介绍 AT模式,在工作中也最常用,用起来也比较简单。

3.Seata 术语表

(术语即:名词介绍,以下内容摘自:Seata 官方文档 http://seata.io/zh-cn/docs/overview/terminology.html)
Spring Cloud Alibaba Seata 分布式事务解决方案简介_第2张图片
Spring Cloud Alibaba Seata 分布式事务解决方案简介_第3张图片
Seata管理的分布式事务的典型生命周期:

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
  2. XID 在微服务调用链路的上下文中传播;
  3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
  4. TM 向 TC 发起针对 XID 的全局提交或回滚决议;
  5. TC 调度 XID 下管辖的全局分支事务,完成分支提交或回滚请求。

4. Seata 功能

Spring Cloud Alibaba Seata 分布式事务解决方案简介_第4张图片

5. AT 模式

(重点介绍AT模式,使用简单,最常用。以下内容摘自:Seata 官方文档 http://seata.io/zh-cn/docs/overview/what-is-seata.html,此处简单介绍一下 AT模式,其他模式参考官网)

Ⅰ.前提

  • 基于支持本地 ACID 事务的关系型数据库。
  • Java 应用,通过 JDBC 访问数据库。

Ⅱ.优缺点

  1. 一个 @GlobalTransactional 注解走天下(优点)
  2. 基于数据库方式,需要在数据库中建 seata 库,业务库见 undo_log 表(缺点)

Ⅲ.整体机制

两阶段(2PC)提交协议的演变:

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

  • 二阶段

    1.提交异步化,非常快速地完成。

    2.回滚通过一阶段的回滚日志进行反向补偿。

Ⅳ.写隔离

  • 一阶段本地事务提交前,需要确保先拿到 全局锁
  • 拿不到 全局锁 ,不能提交本地事务。
  • 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。

以一个示例来说明:

两个全局事务 tx1tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。

  tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的 全局锁本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁
Spring Cloud Alibaba Seata 分布式事务解决方案简介_第5张图片
tx1 二阶段全局提交,释放 全局锁 。tx2 拿到 全局锁 提交本地事务。
Spring Cloud Alibaba Seata 分布式事务解决方案简介_第6张图片
  如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行 反向补偿 的更新操作,实现分支的回滚。

  此时,如果 tx2 仍在等待该数据的 全局锁,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会一直重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务释放本地锁,tx1 的分支回滚最终成功。

  因为整个过程 全局锁 在 tx1 结束前一直是被 tx1 持有的,所以不会发生 脏写 的问题。

Ⅴ.读隔离

  在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)

  如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。
Spring Cloud Alibaba Seata 分布式事务解决方案简介_第7张图片
  SELECT FOR UPDATE 语句的执行会申请 全局锁 ,如果 全局锁 被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到 全局锁 拿到,即读取的相关数据是 已提交 的,才返回。

  出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。

Ⅵ.工作机制

以一个示例来说明整个 AT 分支的工作过程。

业务表:product

Field Type Key
id bigint(20) PRIMARY
name varchar(100)
since varchar(100)

AT 分支事务的业务逻辑:

update product set name = 'GTS' where name = 'TXC';

一阶段

过程:

  1. 解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where name = ‘TXC’)等相关的信息。
  2. 查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。

select id, name, since from product where name = 'TXC';

得到前镜像:

id name since
1 TXC
  1. 执行业务 SQL:更新这条记录的 name 为 ‘GTS’。
  2. 查询后镜像:根据前镜像的结果,通过 主键 定位数据。

select id, name, since from product where id = 1;

得到后镜像:

id name since
1 GTS 2014
  1. 插入回滚日志:把前后镜像数据以及业务 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"
}
  1. 提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局锁
  2. 本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。
  3. 将本地事务提交的结果上报给 TC
二阶段-回滚
  1. 收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
  2. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
  3. 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。
  4. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:

update product set name = 'TXC' where id = 1;

  1. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。
二阶段-提交
  1. 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
  2. 异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

Seata 1.3.0 2020.07.14 发布,截止今天 2020.07.30 已半月有余,关于 Seata 官方部署 新人文档 无力吐槽,Seata 老版本学过来的部署比较简单,初学者直接部署 1.x 以上版本好困难的说,我研究了2天才全套部署成功,以博客的方式分享给大家。

下一篇:Spring Cloud Alibaba Seata 1.3.0 部署


博主写作不易,加个关注呗

求关注、求点赞,加个关注不迷路 ヾ(◍°∇°◍)ノ゙

我不能保证所写的内容都正确,但是可以保证不复制、不粘贴。保证每一句话、每一行代码都是亲手敲过的,错误也请指出,望轻喷 Thanks♪(・ω・)ノ

你可能感兴趣的:(#,Spring,Cloud)