一、概述
指事务的操作位于不同的节点上,需要保证事务的ACID原则,如:订单与库存不在同一个节点上,但需要保证两个事务的一致性(下单—减少库存,取消订单—库存恢复)
二、实现分布式事务的方式
1、两段提交(2PC)
需要数据库厂商的支持,java组件有atomikos等
两阶段提交(Two-phase Commit),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务
准备阶段
协调者询问参与者事务是否执行成功,参与者返执行回结果
提交阶段
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则反之,即回滚事务
注:参与者在准备阶段执行成功,但未提交事务,只有在提交阶段收到协调者的消息,才会执行提交或回滚
存在的问题
1、同步阻塞,所有参与者在等待其他参与者相应的时候都处于同步阻塞状态,无法进行其他操作
2、单点问题,协调者在2PC中起到非常大的作用,产生故障时影响也非常大
3、数据不一致,在阶段二,如果协调者只发送了部分Commit消息,此时如果产生网络异常,则只有接收到Commit消息的参与者提交了事务,使得数据不一致
4、任意一个节点失败,就会导致整个事务失败,容错机制不完善
2、补偿事务(TCC)
针对每个操作,都注册一个与其对应的确认和补偿(撤销)操作,分为三个阶段:
- Try 对业务系统做检测及资源预留
- Confirm 对业务系统做确认提交,Try阶段执行成功并开始执行Confirm阶段时,默认Confirm阶段是不会出错的(只要Try成功,Confirm一定成功)
- Cancel 在业务执行错误,需要回滚时执行业务的取消,释放预留资源
优缺点
- 实现流程相较于2PC简单,但数据一致性比2PC要更差一些
- TCC属于应用层的一种补偿方式,所以在实现业务的时候需要编写很多的补偿代码,在一些业务场景中,业务处理不太友好
3、本地消息表(异步确保)
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用消息队列保证最终一致性。
- 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中
- 之后将本地消息表的消息转发到Kafka等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发
- 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作
优缺点
1、避免了分布式事务,实现数据一致性
2、消息表会耦合到业务系统中,耦合度太高
4、MQ事务消息
一些第三方的MQ是支持事务消息的,如RocketMQ,它们支持事务的方式类似于二阶段提交,流程大致为:
- 第一阶段:Prepared消息,拿到消息的地址;第二阶段:执行本地事务;第三阶段:通过第一阶段拿到的地址去访问消息,并修改状态
- 也就是说在业务方法内要让消息队列提交两次请求,一次发送消息,一次确认消息。如果确认消息发送失败了,RabbitMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RabbitMQ会根据发送端设置的策略来决定回滚还是继续发送确认消息,这样就保证了消息发送与本地事务同时成功或同时失败
优缺点
1、实现最终一致性,不需要依赖本地数据库事务
2、实现难度大,主流MQ不支持
评论 (0)