分布式事务

作者: Djbfifjd | 来源:发表于2021-01-16 20:28 被阅读0次

    一、简述

    分布式事务就是要在分布式系统中实现事务,它其实是由多个本地事务组合而成。可以理解为事务的操作位于不同的节点,需要保证事务的 AICD 特性。例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。如何应对呢?

    二、两阶段提交(Two-phase Commit,2PC)

    两阶段提交是一种强一致性设计,通过引入协调者(Coordinator)来协调参与者(也可称之为各本地资源)的提交和回滚,并最终决定这些参与者是否要真正执行事务。两阶段分别指的是准备(投票)阶段和提交阶段。

    1️⃣运行过程

    1. 准备阶段:协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
    2. 提交阶段:如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。

    2️⃣存在的问题

    1. 同步阻塞:所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。

    2. 单点问题:协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。

    3. 数据不一致:在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。

    4. 太过保守:任意一个节点失败就会导致整个事务失败,没有完善的容错机制。

    三、补偿事务(TCC)

    1️⃣TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

    1. Try 阶段主要是对业务系统做检测及资源预留。

    2. Confirm 阶段主要是对业务系统做确认提交。Try 阶段执行成功并开始执行 Confirm 阶段时,默认 Confirm 阶段是不会出错的。即:只要 Try 成功,Confirm 一定成功。

    3. Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

    2️⃣例:假入 A 要向 B 转账,思路大概是:有一个本地方法,里面依次调用:

    1. 首先在 Try 阶段,要先调用远程接口把 B 和 A 的钱给冻结起来。
    2. 在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
    3. 如果第 2 步执行成功,那么转账成功;如果第二步执行失败,则调用远程冻结接口对应的解冻方法(Cancel)。

    3️⃣优点: 跟 2PC 比起来,实现以及流程相对简单了一些,但数据的一致性比 2PC 也要差一些。

    4️⃣缺点:比较明显,在 2、3 步中都有可能失败。TCC 属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用 TCC 不太好定义及处理。

    四、本地消息表(异步确保)

    1️⃣本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。

    1. 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。
    2. 之后将本地消息表中的消息转发到消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。
    3. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。

    2️⃣优点:一种非常经典的实现,避免了分布式事务,实现了最终一致性。

    3️⃣缺点:消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

    五、MQ 事务消息

    1️⃣有的第三方 MQ 是支持事务消息的,比如 RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的 MQ 都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。

    2️⃣以阿里的 RocketMQ/MetaQ 中间件为例,其思路大致为:

    第一阶段 Prepared 消息,会拿到消息的地址。第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。

    也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了 RocketMQ 会定期扫描消息集群中的事务消息,这时候发现了 Prepared 消息,它会向消息发送者确认,所以生产方需要实现一个 check 接口,RocketMQ 会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

    3️⃣优点:实现了最终一致性,不需要依赖本地数据库事务。

    4️⃣缺点:实现难度大,主流 MQ 不支持,RocketMQ 事务消息部分代码也未开源。

    相关文章

      网友评论

        本文标题:分布式事务

        本文链接:https://www.haomeiwen.com/subject/nixoaktx.html