关于分布式事务:分布式事务分析以及seata的AT模式解读

32次阅读

共计 3814 个字符,预计需要花费 10 分钟才能阅读完成。

前言

家喻户晓,分布式事务是个简单的问题,有很多种不同的思路和办法。

目前,只有是微服务架构做的分布式系统,就绕不开分布式事务这个话题。当然,并不是应用了分布式事务解决方案服务就稳固,高大上,不应用分布式事务服务也并不一定不好。目前咱们公司就没有应用分布式事务,一样撑起了稳固,灵便的零碎。分布式事务的抉择与否没有对错,在于实在我的项目中权衡利弊后的抉择。

分布式事务倒退起源

最开始咱们的利用是单体服务,在应用切当的状况下,spring 的事务能够很好的帮咱们解决相干事务问题。简略剖析 spring 的事务:当某个代理办法应用 @Transactiona 并被 AOP 切面切到当前。spring 会给 aop 切面中应用 ThreadLocal 治理同一个 DBConnection。由这一个 connection 替咱们治理事务:即整个办法执行完结(活异样)则一起提交或者回滚事务。此时咱们的利用如下图:


或者这样

然而随着我的项目流量的增长,同一个 DB 连贯必定承接不住各类服务数据,因而,在理论的我的项目中,咱们大多数的利用与 DB 关系是这样的:

咱们晓得,spring 事务是在一个 jvm 下通过同一个 db 连贯管制的事务,然而当有多台服务器的时候,不同的服务器有本人的 jvm,即便近程调用,db-connection 必定不是同一个。由此,咱们须要一个第三者去协调每个服务的数据连贯。便产生了咱们常说的分布式事务问题。

筹备知识点

  1. ACID

    原子性(Atomic)一致性(Consistency)隔离性(Isolation)持久性(Durability)
  2. 隔离级别

    Read Uncommitted(读未提交)
    Read Committed(读已提交)
    Repeated Read(反复读)
    Serialization(串行化)
  3. CAP 定律

    一致性(C)在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点拜访同一份最新的数据正本)可用性(A)在集群中一部分节点故障后,集群整体是否还能响应客户端的读写申请。(对数据更新具备高可用性)分区容错性(P)以实际效果而言,分区相当于对通信的时限要求。零碎如果不能在时限内达成数据一致性,就意味着产生了分区的状况,必须就以后操作在 C 和 A 之间做出抉择。
  4. BASE 实践
    BASE 是 Basically Available(根本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的缩写。BASE 实践是对 CAP 中一致性和可用性衡量的后果,其来源于对大规模互联网零碎分布式实际的总结,是基于 CAP 定理逐渐演变而来的。BASE 实践的核心思想是:即便无奈做到强一致性,但每个利用都能够依据本身业务特点,采纳适当的形式来使零碎达到最终一致性。
根本可用
    根本可用是指分布式系统在呈现不可预知故障的时候,容许损失局部可用性—- 留神,这绝不等价于零碎不可用。比方:(1)响应工夫上的损失。失常状况下,一个在线搜索引擎须要在 0.5 秒之内返回给用户相应的查问后果,但因为呈现故障,查问后果的响应工夫减少了 1~2 秒(2)零碎性能上的损失:失常状况下,在一个电子商务网站上进行购物的时候,消费者简直可能顺利完成每一笔订单,然而在一些节日大促购物顶峰的时候,因为消费者的购物行为激增,为了爱护购物零碎的稳定性,局部消费者可能会被疏导到一个降级页面

软状态
    软状态指容许零碎中的数据存在中间状态,并认为该中间状态的存在不会影响零碎的整体可用性,即容许零碎在不同节点的数据正本之间进行数据同步的过程存在延时

最终一致性
    最终一致性强调的是所有的数据正本,在通过一段时间的同步之后,最终都可能达到一个统一的状态。因而,最终一致性的实质是须要零碎保障最终数据可能达到统一,而不须要实时保证系统数据的强一致性。

开源的分布式事务框架

目前世面上用于开源的分布式事务问题的框架还不少,比方 seata,lcn, 应用事务音讯 rocketmq 等等。而每种框架上面反对的模式又有很多种(能够想想为什么每种框架都反对好几种模式)。明天咱们具体解读一下 seata 的无代码侵入式的 AT 模式。

首先通过一个简略的 sata 例子认识一下

============user 模块 =============
/**
 * @author liuliang
 * @date 2021/7/9 2:31 下午
 */
@Slf4j
@Service
public class UserService extends ServiceImpl<UserMapper, UserInfo> {

    @Resource
    private OrderFeign orderFeign;

    @GlobalTransactional
    public void buyProduct() {UserInfo userInfo = this.getById(1L);
        userInfo.setAmount(userInfo.getAmount() - 1);
        // 扣减本人的余额
        this.updateById(userInfo);
        // 创立订单
        orderFeign.test();
        log.info("购买完结");
    }
}

user 模块调用 order 模块

/**
 * @author liuliang
 * @date 2021/7/9 2:58 下午
 */
@FeignClient(name = "mm-order")
public interface OrderFeign {@GetMapping("/order/test")
    void test();}

order 模块

/**
 * @author liuliang
 * @date 2021/7/9 3:05 下午
 */
@Slf4j
@Service
public class OrderService extends ServiceImpl<OrderMapper, OrderInfo> {public void test() {OrderInfo orderInfo = new OrderInfo();
        orderInfo.setNum(1);
        orderInfo.setPrice(100L);
        orderInfo.setSkuName("小视频");
        this.save(orderInfo);
        log.info("小视频数据已保留");
//        throw new RuntimeException("111");
    }

}

能够看到,当 user 模块退出了 seata 的 @GlobalTransactional 注解后,当 order 模块异样,user 会回滚,属于两个不同的 jvm 的不同代理连贯竟然能在同一个事务外面!接下来咱们剖析一下 seata 的原理

  • seata 框架

能够看到,seata 有三个组件:

1. TM(Transaction Manager)事务管理者
2. RM(Resource Manager)数据源管理者
3. TC(Transaction Coordinator)事务协调器

通常,一个分布式事务的流程能够剖析为以下步骤:

1. TM 向 TC 申请开启一个全局事务,全局事务创立胜利并生成一个全局惟一的 XID。2. XID 在微服务调用链路的上下文中流传。3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖。4. TM 向 TC 发动针对 XID 的全局提交或回滚决定。5. TC 调度 XID 下管辖的全副分支事务实现提交或回滚申请。

在具体分析 seata 执行流程之前,先将简略的 demo 别离演示:
user 调用 order 前异样
user 调用 order 时异样
user 调用 order 后异样(order 处打断点)
user 调用 order 后失常
几种状况。
接下来咱们通过问题的模式,来逐渐解开纳闷:
1.undo_log 是什么,是什么时候产生的?又是什么时候隐没?


io.seata.rm.datasource.undo.mysql.MySQLUndoLogManager#insertUndoLogWithNormal 
io.seata.tm.api.TransactionalTemplate#execute
io.seata.core.rpc.netty.RmMessageListener#onMessage
通过浏览源码发现,seata 在数据源代理层,在本地事物提交之前,向 undo_log 表插入批改前和批改后的记录。在切面最初,如果事务失常,则触发全局提交,全局提交时,tm 告诉 tc,tc 查到各个分支事务,告诉删除 undo_log 数据
          如果事务异样,则登程全局回滚,全局回滚是,tm 告诉 tc,tc 查到各个分支事务,告诉各个分支回滚到执行前的数据,而后删除 undlog

2. 会存在 undo_log 与本地事物不统一的状况吗?

不会, 本地事物里,业务逻辑和 undo_log 在同一个事务外面

3. 如果一条数据在执行的过程中被另一个事务批改了,那数据就错乱码了吗?(或者说隔离性)

写 / 读隔离,解释全局锁,本地锁

4. 为什么 seata 或者说 lcn 会有多种模式去解决分布式事务?

能力边界,seata 会须要原生数据库反对事务,并且有主键

总结下 seata 的 AT 模式:
Seata 的 AT 模式基于本地事务的个性,通过拦挡并解析 SQL 的形式,记录自定义的回滚日志,从而突破 XA 协定阻塞性的制约,在一致性、性能、易用性 3 个方面获得均衡:在达到确定一致性(非最终统一)的前提下,即保障较高的性能,又能齐全不侵入业务。
在绝大部分利用场景下,Seata 的 AT 模式都能很好地发挥作用,把利用的分布式事务反对老本降到极低的程度。

正文完
 0