分布式事务
分布式事务
两阶段提交(XA)
两阶段提交需要一个事务管理器,来协调两个阶段。
- 事务管理器会在每个数据库进行预提交,检查是否预提交都就绪
- 如果都就绪,则让每个数据库提交事务,否则回滚事务
- 尽量保证了数据的一致性
- 事务管理器有单例问题,宕机了整个系统不可用
- 在第二步里通知提交或者回滚都可能通知失败,导致数据不一致
- 在第二步里通知到一半,事务管理器宕机选举,新主节点不知道那些服务还没通知
- 依赖于数据库,性能低
- 不符合微服务设计的每个微服务只能处理自己的数据库
TCC(Try Confirm Cancel)
- Try:对各个服务的资源进行检查和冻结
- Confirm:将冻结转为执行具体的操作
- Cancel:在确认阶段失败了,执行已成功的操作的补偿操作
- 回滚依赖于自己实现的补偿代码
- 适合于钱等需要较强一致性的场景
- 需要分布式事务整个流程较短
Saga
saga的流程是依次执行提交各个服务的本地事务,如无意外都执行成功则分布式事务完成。
如果其中有一个事务失败,则对已经执行成功的事务进行补偿操作。
- 适合于分布式事务流程比较长
- 一致性比TCC差
- 不保证事务的隔离性
TCC与Saga
- 在TCC的第一步中,全部本地事务都是还没提交的。而Saga里依次执行本地事务是执行就提交的
- TCC还需要先进行try操作进行冻结。而Saga是直接执行提交,所以少了一次调用
- 因为TCC需要等待全部本地事务进行冻结,所以在这个阶段资源会都被阻塞,需要较短的分布式事务。
而Saga提交的颗粒度是本地事务,所以适合于较长的分布式事务 - 因为TCC是在一步里确认全部的本地事务,所以一致性强于Saga的逐个本地事务进行提交
- 因为TCC在try里阻塞了资源,所以分布式事务之间是隔离的。而Saga的逐个本地事务则不隔离
本地消息表
- A服务在本地事务里除了进行业务操作,还在一个消息表里插入一条数据,然后往MQ里丢一条消息
- A服务也会定期扫描消息表,把消息表的消息丢到MQ里
- B服务从MQ收到消息后执行本地事务。如果成功则去除A服务里消息表对应的数据,否则通知A服务执行补偿操作
- 依赖于数据库管理事务,性能低
最大努力通知
适合于不支持消息事务的MQ。
- A服务执行本地事务,执行成功后往MQ里丢一条消息
- 有一个最大努力通知服务来专门消费MQ的消息,去调B服务来执行本地事务
- 如果B服务执行成功,则分布式事务完成,否则重试几次,重试不行就放弃
可靠消息服务
需要使用一个MQ来实现,其功能目前只有RocketMQ支持消息事务。
- A服务向MQ丢一条消息,MQ只先持久化消息,不放到队列里。如果失败了则取消事务
- 丢成功以后,A服务执行本地事务。如果执行成功通知MQ把消息放到队列里,否则丢弃消息取消事务
- 其中A服务对MQ的调用如果丢失了,MQ会在消息投递后的一段时间后,回调A服务的接口,查询A服务的本地事务是执行已提交、已回滚还是执行中
- 之后B服务就会从MQ里收到消息,然后同步执行本地事务。成功的话向MQ提交,分布式事务完成,否则B服务会有个重试次数,再不行则报警
- A服务的本地事务与MQ同步是异步的,以提高A服务的并发
- A服务与MQ的异步消息丢失通过MQ回调来弥补
参考文章:
分布式事务
https://cellargalaxy.github.io/posts/分布式/4.分布式事务/