
分布式事务
rocketmq的最终一致性
主要是依靠rocketmq事务消息的 事务状态回查 机制,外加消费者那边的mq重试机制来确保分布式事务的最终一致性。
你可以这样理解,首先,先发一个Half Message
(半消息),然后再执行本地的业务逻辑,本地执行完成之后,再将半消息转成正式的消息, 以确保生产者发送消息和本地业务的一致性。
如果半消息发送成功,但是业务执行失败怎么办呢?这时候就用到了,事务状态回查 机制,broker会来检查这个半消息的状态,来 确保这个消息到底是丢弃还是发送。
❌ 为什么“先事务,后发消息”的方案不可靠?
想象一下这个流程:
- 订单服务开始本地数据库事务。
- 在事务中插入订单记录。
- 提交本地数据库事务。(关键点!)
- 订单服务调用 RocketMQ,发送一条普通消息(通知积分服务增加积分)。
问题出在哪里? 就在第3步和第4步之间!“提交事务”和“发送消息”这两个操作无法构成一个原子操作。
所以,RocketMQ事务消息的本质,是利用MQ自身的可靠性和主动回查能力,来弥补“数据库事务”和“网络调用”之间的鸿沟,从而实现分布式系统下的最终一致性。
Seata
- AT 模式 :适用于大多数场景,对代码侵入性低,但是需要建
undo_log
表 - TCC 模式:适用于对性能要求极高、或者数据库不支持事务的场景(如分库分表),但需要自己实现 Try、Confirm、Cancel 三个方法,代码侵入性强。
Seata 其实是2PC的一种实现,不过它的特点是基于本地提交的,然后有一个全局事务管理器(也就是Seata服务)来控制,每个子服务都 需要建 undo_log
表,先由接口服务的发起者向Seata申请一个xid(全局事务id)然后一路带下去,Seata就知道本次有多少个子服务了, 如果接口没有报错,那么就删除undo_log
表的数据,如果有报错,则根据undo_log
表的数据进行回滚操作。
可以这样理解:
- TC(事务协调器) 就像一个 会议主持人。
- XID 是本次会议的 会议号。
- 各个微服务(RM) 是 参会人员。
- @GlobalTransactional 方法 是 会议议程。
- 开会前,主持人(TC)并不知道谁会来参会。
- 会议开始(全局事务开始),主持人公布会议号(XID)。
- 每个参会者(RM)听到会议号后,自己主动签到(注册分支事务):“我是XXX,我来参加这个会了”。
- 主持人(TC)的签到表上就有了所有参会者名单(分支事务列表)。
- 会议议程(业务代码)一项项进行。
- 直到所有议程进行完毕(方法执行完),会议主席(TM)宣布:“散会!”(全局提交)。主持人就通知所有签过到的人:“会议结束,你们可以走了”(异步通知提交)。
- 如果会议中途出了状况(抛出异常),会议主席(TM)宣布:“会议中止!”(全局回滚)。主持人就通知所有签过到的人:“按照会议记录,恢复原样!”(异步通知回滚)。
所以,是的,全局事务管理器(TC)会等到接口执行完(或发现有异常)后,才向各个子服务发送全局提交或回滚的通知。 而它之所以知道要通知谁,是因为子服务们在执行过程中已经主动向它“签到”了。