分布式事务

分布式事务

  • 解释:相对于数据库事务,分布式事务主要针对于多个服务、多个数据库的数据一致性问题。分布式应用之间存在复杂的调用,其实就是服务之间数据的同步,分布式事务就是保障数据同步的一致性。
  • 分布式事务理论:CAP理论、BASE理论
  • XA、TCC、可靠消息实现最终一致性
  • XA:通过事务管理器协调各个服务的事务,通过2PC流程完成事务。主要分为提交请求阶段提交执行阶段;在提交请求阶段进行所有事务操作,并将Undo信息和Redo信息写入日志;在提交执行阶段进行操作的提交或回滚;
  • TCC:主要是达到最终一致性目的,不会长期持有锁。分为三个阶段进行事务管理:try、comfirm、cancel,所以需要对应三个接口,在try阶段就完成了事务操作并且提交。comfirm\cancel阶段则是对try阶段数据的确认和回滚。相比之下xa需要数据库支持且事务期间长期持有锁,tcc只是确认
  • 可靠消息的最终一致性:其实就是通过消息中间件进行数据同步,并且提供各个阶段的数据确认以保证一致性;
  • 事务发起时分为prepare、confirm两个阶段,只有confirm阶段确认后事务才生效传递到消费方服务
  • 消费者消费事务消息,并返回事务结果ack
  • 使用定时任务检查状态卡死不动的事务消息,如:有prepare没有confirm,有comfirm没有ack、有ack没有生产者端ack确认的

两阶段2PC

包含协调者参与者两个角色

  1. 准备阶段:各个参与者预执行事务,执行成功则返回可提交事务表决并等待,否则返回不可提交事务表决;
  2. 提交阶段:协调者收到所有参与者都返回可提交后,向参与者发出提交命令,提交所有参与者的事务;

存在的问题:

  1. 性能问题:参与者在第一阶段后需要等待协调者的提交/回滚指令,虽然一致性得到了一定的保证但是事务长时间占用资源降低了系统性能
  2. 一致性问题:协调者、参与者同时出现宕机或者网络分区时,可能导致数据不一致
    1. 协调者正常发起事务,参与者预执行事务成功
    2. 其中一个参与者预执行事务失败并且这时候挂了,同时协调者也挂
    3. 由于参与者和协调者同时挂了,于是其他参与者不知道他行了什么操作。其他参与者都返回了成功,但是协调者挂了没有发送下一步指令;
    4. 其他参与者投票选出新的协调者,新协调者发现存活的参与者第一阶段都是成功,于是接入第二阶段进行事务提交
    5. 挂掉的参与者、协调者恢复正常,这时候就出现事务不一致的情况
    6. 原因:根本原因就是参与者只关注自己的事务,其他参与者对自己来说是透明的,无法感知到其他参与者的状态,需要引入一个机制让参与者能够感知到其他参与者的状态

三阶段提交 3PC

3PC是2PC的优化,将2PC第一阶段拆成两个阶段,先资源加锁、再执行事务、最后再提交事务

3PC过程:

  1. CanCommit阶段:事务询问阶段,询问参与者是否可以进行事务,一般是进行资源上加锁。这个阶段可以确定参与者存活状态、资源是否满足;
  2. PreCommit阶段: 事务预提交阶段,参与者执行事务操作,生成redo、undo日志
  3. DoCommit阶段 :提交事务

针对2PC问性能、一致性问题,3PC做了两个方面的改进

  1. 阻塞性能问题:引入超时机制,协调者、参与者都引入超时,协调者超时未收到参与者反馈是默认参与者失败,参与者超时未收到协调者下一步指令时默认成功自动提交事务,这也可能会导致数据不一致,需要考量超时时长的问题

  2. 一致性问题:由于3PC的每个阶段也是所有参与者都成功才会进入到下一个阶段,有如下分析

    1. 第一阶段服务挂了:第一阶段没有任何数据操作,所以挂了也没问题,直接事务失败
    2. 第二阶段服务挂了:由于所有参与者都参与了第一阶段并且都成功,所以协调者可以按照正常提交,备份协调者通过其他参与者也可以知道所有参与者第一阶段是成功的(不然也不会进入到阶段2);宕机的参与者恢复用询问协调者事务结果,按照结果执行即可。不会导致数据不一致
    3. 第三阶段服务挂了:即便协调者或协调者备份未收到宕机参与者的commit ACK,也结束该次事务;宕机的参与者恢复后发现收到commit或者precommit,也将自行commit该次事务

    所以3PC可以解决2PC的一致性问题,通过预提交预先判断事务的可执行性再判断是否进行事务

TCC

应用层的两阶段提交,第一阶段try,第二阶段confirm/cancel

(1)第一阶段:Try,业务系统做检测并预留资源 (加锁,锁住资源),比如常见的下单,在try阶段,我们不是真正的减库存,而是把下单的库存给锁定住。

(2)第二阶段:根据第一阶段的结果决定是执行confirm还是cancel

  • Confirm:执行真正的业务(执行业务,释放锁)
  • Cancle:是对Try阶段预留资源的释放(出问题,释放锁)

注意事项

  • 允许空回滚:空回滚出现的原因是 Try 超时或者丢包,导致调用者直接触发二阶段的Cancel ,此时事务**参与者未收到Try却收到了Cancel 请求**,所以 cancel 接口在实现时需要允许空回滚,也就是 Cancel 执行时如果发现没有对应的事务 xid 或主键时,需要返回回滚成功,让事务服务管理器认为已回滚。

  • 防悬挂控制:悬挂指的是**二阶段的Cancel比 一阶段的Try 操作先执行,出现该问题的原因是 Try 由于网络拥堵而超时,导致事务管理器生成回滚触发 Cancel 接口,但之后拥堵在网络的 Try 操作被资源管理器收到了,但是 Cancel 比 Try 先到。但按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,所以此时应该拒绝执行空回滚之后到来的 Try 操作,否则会产生数据不一致。因此我们可以在 Cancel 空回滚返回成功之前,先记录该条事务 xid 或业务主键**,标识这条记录已经回滚过,Try 接口执行前先检查这条事务xid或业务主键是否已经标记为回滚成功,如果是则不执行 Try 的业务操作。

  • 幂等控制: 由于**网络原因或者重试操作**都有可能导致 Try - Confirm - Cancel 3个操作的重复执行,所以使用 TCC 时需要注意这三个操作的幂等控制,通常我们可以使用事务 xid 或业务主键判重来控制。

seata

Seata 中有三大模块,分别是 TM、RM 和 TC。 其中 TM 和 RM 是作为 Seata 的客户端与业务系统**集成在一起,TC 作为 Seata 的服务端独立部署**。

Seata服务依赖三个表:global_tablebranch_tablelock_table,内置的全局唯一id生成器用于辅助生成全局事务ID和分支事务ID

Seata有三个组件:

  • 事务管理者(TM)(事务创建):Seata 中用于创建和决议事务结果的实体,一般集成于业务调用链路的上游.
  • 资源管理者(RM)(分支事务):Seata 中用于管理资源的实体,一般情况下等同于微服务中的提供方(provider),管理其所在服务中的资源,如数据库资源等.
  • 事务协调者(TC)(事务决议):Seata 中用于2pc方式的事务模式统一协调事务的实体(SAGA除外),其可由事务管理者驱动或自身驱动进行事务的二阶段行为.

在 Seata 中,分布式事务的执行流程:

  • TM 开启分布式事务(TM 向 TC 注册全局事务记录);
  • 按业务场景,RM加入事务并执行事务(RM 向 TC 汇报资源准备状态 );
  • TM 结束分布式事务,事务一阶段结束(TM 通知 TC 提交/回滚分布式事务);
  • TC 汇总事务信息,决定分布式事务是提交还是回滚;
  • TC 通知所有 RM 提交/回滚 资源,事务二阶段结束;

Seata 会有AT 模式、TCC 模式、Saga 模式和 XA 模式

AT 模式

AT 模式是一种对业务无任何侵入的分布式事务

  • 用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会**自动生成**事务的二阶段提交和回滚操作
  • 一阶段:Seata 拦截“业务 SQL”,解析 SQL 备份更新前的数据(前置镜像),然后执行“业务 SQL”更新数据;业务数据更新后再更新后的数据(后置镜像),最后生成行锁。
  • 二阶段:根据第一阶段的备份数据,进行提价或者回滚,最后删除备份数据
  • 隔离性依赖本地锁,与全局锁

TCC 模式

Saga 模式

适用于业务流程长且需要保证事务最终一致性的业务

XA 模式