在分布式系统中进行分库分表时会设计到分布式事务的问题,如何保证访问两个不同的服务也能实现事务的操作。
- 通过MQ来实现:
从消息生产者的角度来看,首先操作数据库,若数据库执行失败,则抛出异常,若数据库执行成功,则操作消息队列,消息队列发送消息供消费者接收,若消息队列发送失败,则抛出异常,异常中执行回滚操作。
public void transational{
try{
//首先操作数据库
boolean result = dao.update();//数据库执行失败则抛出异常
//若执行成功则操作消息队列
if(result){
//发送消费
mq.append(model);//若发送消息失败,则抛出异常,执行回滚操作
}
}catch(Exception e){
//抛出异常回滚
rollback();
}
}
这是针对生产者,对于消费者会出现一下问题
1. 消息出列后,消费者对应的业务操作要执行成功。如果业务执行失败,消息不能失效或者丢失。需要保证消息与业务操作一致
2. 尽量避免消息重复消费。如果重复消费,也不能因此影响业务结果
第二种方式:
下面以阿里巴巴的 RocketMQ 中间件为例,分析下其设计和实现思路。
RocketMQ 第一阶段发送 Prepared 消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。细心的读者可能又发现问题了,如果确认消息发送失败了怎么办?RocketMQ 会定期扫描消息集群中的事物消息,这时候发现了 Prepared 消息,它会向消息发送者确认,Bob 的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ 会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。如下图: