04-消息实现分布式事务
问题
场景:订单系统创建订单成功,需购物车系统清空购物车中的订单
业务执行步骤:
- 在订单库中插入一条订单数据,创建订单;
- 发消息给消息队列,消息的内容就是刚刚创建的订单;
在分布式系统中上述两个步骤都有可能失败:
- 创建了订单,没有清理购物车;
- 订单没创建成功,购物车里面的商品却被清掉了;
需要解决的问题:上述任意步骤都有可能失败的情况下,还要保证订单库和购物车库这两个库的数据一致性。
对于购物车系统收到消息清理购物车处理比较简单,只有成功删除购物车才提交消息确认,否则重试
关键是在订单系统中的 创建订单和发送消息的两个步骤如何确保同时成功或失败
什么是事务
严格意义上的事务具有4个属性:原子性、一致性、隔离性、持久性
- 原子性:操作不能再分割,要么成功要么失败,不能一半成功一半失败
- 一致性:是指这些数据在事务执行完成这个时间点之前,读到的一定是更新前的数据,之后读到的一定是更新后的数据,不应该存在一个时刻,让用户读到更新过程中的数据。
- 隔离性:是指一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对正在进行的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性:是指一个事务一旦完成提交,后续的其他操作和故障都不会对事务的结果产生任何影响。
分布式事务
是在分布式系统中实现事务,在分布式事务中严格按事务的标准来要求是不可能实现的,光要实现数据的一致性就非常困难,最终只能保证顺序的一致性、以及最终一致性
常见的分布式实现方式
- 2PC(Two-phase Commit,也叫二阶段提交)
- TCC(Try-Confirm-Cancel)
- 事务消息
每一种实现都有其特定的使用场景,也有各自的问题,都不是完美的解决方案
2pc和tcc这里不做讨论,主要来看下事务消息
消息队列如何实现事务
事务消息需要消息队列提供相应的功能才能实现,rocketmq和kafka都提供了事务相关功能
回到订单系统的例子看如何实现:
消息事务
步骤:
- 1、开启一个消息事务
- 2、订单系统给消息服务发送一个半消息
半消息的消息内容是完整的只是在事务没有提交之前消费者是不可见的
- 3、执行本地事务创建订单
- 4、提交或回滚消息事务
如果创建订单成功则提交消息事务,消息会投递到购物车系统,创建失败则回滚事务消息
问题:提交消息事务失败怎么办
kafka比较粗暴,直接抛出异常让用户自己解决,我们可以在业务代码中反复提交知道提交成功,或删除原来创建成功的订单进行补偿;rocketmq提供了另外一种解决方式
rocketmq分布式事务实现
事务反查机制
反查机制rocketmq提供了事务反查的机制,如果因网络或其他原因导致rocketmq没有收到订单系统的事务的提交或者回滚,会定期取订单系统反查这个消息事务对应的本地事务的状态,从而来确定事务是提交还是回滚,为了支持反查,业务代码需要实现一个反查本地事务状态的接口
最后
kafka需要自己写代码来解决问题,rocketmq通过反查机制解决,并不代表rocketmq事务功能比kafka好,只能说在此例场景更适合,Kafka 对于事务的定义、实现和适用场景,和 RocketMQ 有比较大的差异,后面的课程中,我们会专门讲到 Kafka 的事务的实现原理。
内容来源说明:文章中的部分内容以及图片来自《极客时间-消息队列高手课》,写文章目的只是作为学习后的总结和整理