分布式事务
1.1 两阶段提交(2PC)
当应用逐渐扩展,出现一个应用使用多个数据源的情况,这个时候本地事务已经无法满足数据一致性的要求。由于多个数据源的同时访问,事务需要跨多个数据源管理,分布式事务应运而生。其中最流行的就是两阶段提交(2PC),分布式事务由事务管理器(TM)统一管理。
两阶段提交分为准备阶段和提交阶段。
两阶段提交-commit
两阶段提交-rollback
在 JavaEE 平台下,WebLogic、Webshare 等主流商用的应用服务器提供了 JTA 的实现和支持。而在 Tomcat 下是没有实现的,这就需要借助第三方的框架 Jotm、Automikos 等来实现,两者均支持 Spring 事务整合。
然而,两阶段提交也不能完全保证数据一致性问题,并且有同步阻塞的问题,所以其优化版本三阶段提交(3PC)被发明了出来。
1.2 三阶段提交(3PC)
然而3PC也只能保证绝大多数情况下的数据一致性。
2.spring中的分布式事务处理
spring中的分布式事务可以使用JTA来处理,多个数据源事务的同步。但是,JTA实现的分布式事务,有一定的性能问题。所以,很多时候都是不使用JTA来实现分布式事务的。
因此,大多时候,用最终一致性代替强一致性。
- 两阶段型 2PC
就是分布式事务两阶段提交,对应技术上的XA、JTA/JTS。这是分布式环境下事务处理的典型模式。
- 补偿型 TCC(Try Confirm Cancel)模式
TCC型事务(Try/Confirm/Cancel)可以归为补偿型;TCC思路是:尽早释放锁;在Try成功的情况下,如果事务要回滚,Cancel将作为一个补偿机制,回滚Try操作;
TCC各操作事务本地化,且尽早提交 (放弃两阶段约束);当全局事务要求回滚时,通过另一个本地事务实现“补偿”行为;
TCC是将资源层的两阶段提交协议转换到业务层,成为业务模型中的一部分;
- sagas模式 Axon 框架实现
将一个长事务,分解成一系列有序的本地事务。每个事务更新数据库或发送消息,来触发下一个本地事务;如果本地事务执行失败,soga会有序执行补偿事务,来回滚之前的操作。
- 异步确保型 本地消息表/消息队列实现
将一些同步阻塞的事务操作变为异步的操作,避免对数据库事务的争用,典型例子是热点账户异步记账、批量记账的处理。
- 可靠消息传递 消息队列实现
前一个事件发生,后一个事件一定发生。
- 最大努力通知型
分布式事务中要求最低的一种, 也可以通过消息中间件实现, 与前面异步确保型操作不同的一点是, 在消息由MQ Server投递到消费者之后, 允许在达到最大重试次数之后正常结束事务.
3.微服务架构的分布式事务处理:
下图中,若调用user微服务出错,则订单操作会被回滚。此时,数据保持一致。
下图中,若调用票仓微服务出错,user微服务则无法进行回滚。
解决方案1:尽可能减少服务间的调用
解决方案2:没有服务间的调用,通过消息驱动调用服务。
基于消息队列实现的微服务分布式事务:
需要注意的问题:
1.消息中间件需要支持事务;
2.如何处理重试的消息:
通过全局id,幂等性校验(确保无论重试几次,返回的结果都应当不会变化,保证多次重试不会对系统造成其他影响。)
3.发生业务异常时的回滚操作
解决1.把出错的失败消息写入一个队列中,进行相应的回滚操作
解决2.通过定时任务检查超时订单,对未完成的订单做自动回滚
解决3.保存出错信息,人工处理。
总结:
使用幂等性UUID,防止错误处理时重试对数据造成多次处理。
分布式锁,防止多次相同请求,短时间内重复处理多次。
1.一般情况下,使用最终一致性即可满足业务上的需求;
2.合理地选择同步还是异步进行处理;
3.合理地设计实现代码:设计的原则为 越容易出现错误的代码,越应当优先进行检查和处理;这样可以保证在对数据修改之前,对可能出现的错误情形都考虑到了,然后尽早地返回结果。
从而尽可能地减小 分布式事务出现错误回滚的情况。
参考文章:
1.《微服务下的数据一致性的几种实现方式之概述》https://www.jianshu.com/p/b264a196b177