分布式事务

分布式事务(三)、柔性事务之 TCC、Saga、本地消息表、事务消息、最大努力通知 - 墨天轮

https://zhuanlan.zhihu.com/p/590834427

本地消息表实现

业界常见方案,因为其他框架太重

主要思想就是将分布式事务拆分为本地事务和消息事务两个部分,本地事务在本地数据库提交or回滚,而消息事务则将消息写入消息中间内,以实现消息可靠投递和顺序性

做法

  • 创建一张本地消息表,用于记录要发的消息内容
  • 将业务表操作和消息表创建记录置于同一事务下(不要把发消息行为放进去,不然可能会因为网络延迟,导致生产者误认为是发送失败,造成异常回滚,实际是发送成功了)
  • 消费者端消费成功后,改本地消息状态

某个环节可能出现的错误

  • 消息投递失败,开定时任务,扫表重投
  • 消息消费失败,依靠消息的重投机制,不断重试
  • 回填状态失败,那么相当于业务数据已经一致了,但消息表里的状态是错的。
    • 定时任务继续重投消息,依靠幂等校验,去推进消息状态
    • 查下游系统的状态,如果成功了,则直接推进消息状态即可

优缺点

优点

- 没TCC,stea,2Pc重
- 扩展性好
- 适用范围广

缺点

- 定时任务,扫表性能较开销大
- 实现复杂度相对较高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Autowired
TransactionTemplate transactionTemplate;

public void order(OrderDTO orderDTO){

boolean transactionSuccess = transactionTemplate.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
try {
orderServive.createOrder(orderDTO);
messageService.createMessage(orderDTO);
//以上执行如果未抛异常,则成功,返回true
return true; // 表示事务执行成功
} catch (Exception e) {
// 如果发生异常,则标记事务为回滚
status.setRollbackOnly();
return false; // 表示事务执行失败
}
}
});

if (transactionSuccess) {
// 事务执行成功,可以执行 mqService.send(orderDTO)
mqService.send(orderDTO);
messageService.updateSuccess(orderDTO);
} else {
// 事务执行失败的处理逻辑
// 可以抛出异常或记录日志等
}
}