分布式事务

在分布式系统中一次操作由多个系统协同完成,这种一次事务操作涉及多个系统通过网络协同完成的过程称为分布式事务。

柔性事务与刚性事务

  • 柔性事务满足BASE理论(基本可用,最终一致)
  • 刚性事务满足ACID理论

解决方案

两阶段提交(2PC)

2020310184042

准备阶段:协调者向参与者发起指令,参与者评估自己的状态,如果参与者评估指令可以完成,则会写redo或者undo日志,然后锁定资源,执行操作,但并不提交

提交阶段:如果每个参与者明确返回准备成功,则协调者向参与者发送提交指令,参与者释放锁定的资源,如何任何一个参与者明确返回准备失败,则协调者会发送中止指令,参与者取消已经变更的事务,释放锁定的资源。

  • 优点:实现强一致性
  • 缺点:整个事务的执行需要由协调者在多个节点之间去协调
    • 单点问题:协调者如果发生故障会造成很大影响
    • 性能问题:所有事务参与者在等待其它参与者响应的时候都处于同步阻塞等待状态,无法进行其它操作
    • 一致性风险:当在提交阶段网络发生异常,只有部分参与者commit了消息,造成数据不一致 任意一个节点失败就会导致整个事务失败

三阶段提交(3PC)

为了缓解2PC的缺点 3PC增加了一个询问阶段

询问阶段:协调者询问参与者是否可以完成指令,协调者只需要回答是还是不是,而不需要做真正的操作,这个阶段超时将导致事务中止

准备阶段

提交阶段

三段式提交对单点问题和回滚时的性能问题有所改善,但是它对一致性风险问题并未有任何改进

共享事务

通过多个服务共用一个数据源的方式来实现,不过这种方式很鸡肋,因为往往数据库才是整个系统的瓶颈

事务补偿(TCC)

批注 2020-07-13 101824

  • 优点:最终保证数据的一致性,在业务层实现事务控制,灵活性好。
  • 缺点:开发成本高,每个事务操作每个参与者都需要实现try/confirm/cancel三个接口。

使用消息队列实现最终一致性

批注 2019-10-31 201018

在这种方案下,只有事务的一部分成功,事务的其他部分如果失败后就不断重试,直至操作成功或者人工介入

最大努力通知方案

类似于第三方支付的支付回调 一直进行重试 直到成功为止

本地消息表

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

202031620440

补偿的方式

  • 生产者一定要将数据投递到MQ服务器中(消息确认机制)
  • MQ消费者消息能够正确消费消息,采用手动ACK模式(当消费者消费消息失败,则不确认消息,消息进行重试)
  • 当生产者出错回滚,发送到补偿队列的消息会检测生产者的数据是否提交成功,如果没有,则补偿队列的消费者会重新执行一遍生产者没有提交的事务

批注 2020-03-16 164628

SAGA

通过将事务拆分为一系列正向原子操作T1 T2 ... TN

与一系列的补偿原子操作:C1 C2 ... CN

这些操作都必须保证是幂等的,当事务发生失败,可以采取两种策略:

  1. 正向恢复 不断重试T 直至成功
  2. 反向恢复 反向执行补偿操作 将数据恢复至原始状态

LCN

原理

2020311161130

使用

  • 启动tx-manager

客户端

  • 依赖
<dependency>
    <groupId>com.codingapi.txlcn</groupId>
    <artifactId>txlcn-tc</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.codingapi.txlcn</groupId>
    <artifactId>txlcn-txmsg-netty</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
  • 配置
@EnableDistributedTransaction
  • 使用

发起者

@LcnTransaction
@Transactional(rollbackFor = Exception.class)
public void consume(){
    jdbcTemplate.update("INSERT INTO tb_order VALUES(1,1,'test')");
    String result = producerRemote.home();
}

参与者

@LcnTransaction
@Transactional(rollbackFor = Exception.class)
public String home() {
    jdbcTemplate.update("UPDATE stock SET stock = stock -1 WHERE product_id = 1");
    return name+port;
}

集群

  • 启动多台tx-manager
  • 发起者与参与者配置地址
tx-lcn.client.manager-address=127.0.0.1:8070,127.0.0.1:8071

results matching " "

No results matching " "

results matching " "

No results matching " "