点击上方“程序员小明”,选择“星标”
今晚可以不加班!
场景
A(存在DB操作)、B(存在DB操作)两方需要保证分布式事务一致性,通过引入中间层MQ,A和MQ保持事务一致性(异常情况下通过MQ反查A接口实现check),B和MQ保证事务一致(通过重试),从而达到最终事务一致性。原理
大事务 = 小事务 + 异步
MQ与DB一致性原理(两方事务)
流程图
![流程图 640?wx_fmt=png](https://img-blog.csdnimg.cn/img_convert/053bff2a64db9585f4ae81d258a90fc9.png)
上图是RocketMQ提供的保证MQ消息、DB事务一致性的方案。
MQ消息、DB操作一致性方案:
发送消息到MQ服务器,此时消息状态为SEND_OK。此消息为consumer不可见。
执行DB操作;DB执行成功Commit DB操作,DB执行失败Rollback DB操作。
如果DB执行成功,回复MQ服务器,将状态为COMMIT_MESSAGE;如果DB执行失败,回复MQ服务器,将状态改为ROLLBACK_MESSAGE。注意此过程有可能失败。
MQ内部提供一个名为“事务状态服务”的服务,此服务会检查事务消息的状态,如果发现消息未COMMIT,则通过Producer启动时注册的TransactionCheckListener来回调业务系统,业务系统在checkLocalTransactionState方法中检查DB事务状态,如果成功,则回复COMMIT_MESSAGE,否则回复ROLLBACK_MESSAGE。
TransactionCheckListener 是在消息的commit或者rollback消息丢失的情况下才会回调(上图中灰色部分)。这种消息丢失只存在于断网或者rocketmq集群挂了的情况下。当rocketmq集群挂了,如果采用异步刷盘,存在1s内数据丢失风险,异步刷盘场景下保障事务没有意义。所以如果要核心业务用Rocketmq解决分布式事务问题,建议选择同步刷盘模式。
多系统之间数据一致性(多方事务)
![多方事务 640?wx_fmt=png](https://img-blog.csdnimg.cn/img_convert/26e88f851f225818a902446e7f429aef.png)
当需要保证多方(超过2方)的分布式一致性,上面的两方事务一致性(通过Rocketmq的事务性消息解决)已经无法支持。这个时候需要引入TCC模式思想(Try-Confirm-Cancel,不清楚的自行百度)。
交易系统创建订单(往DB插入一条记录),同时发送订单创建消息。通过RocketMq事务性消息保证一致性
接着执行完成订单所需的同步核心RPC服务(非核心的系统通过监听MQ消息自行处理,处理结果不会影响交易状态)。执行成功更改订单状态,同时发送MQ消息。
交易系统接受自己发送的订单创建消息,通过定时调度系统创建延时回滚任务(或者使用RocketMq的重试功能,设置第二次发送时间为定时任务的延迟创建时间。在非消息堵塞的情况下,消息第一次到达延迟为1ms左右,这时可能RPC还未执行完,订单状态还未设置为完成,第二次消费时间可以指定)。延迟任务先通过查询订单状态判断订单是否完成,完成则不创建回滚任务,否则创建。 PS:多个RPC可以创建一个回滚任务,通过一个消费组接受一次消息就可以;也可以通过创建多个消费组,一个消息消费多次,每次消费创建一个RPC的回滚任务。 回滚任务失败,通过MQ的重发来重试。
案例分析
单机环境下的事务示意图
如下为A给B转账的例子。
步骤 | 动作 |
---|---|
1 | 锁定B的账户 |
2 | 锁定B的账户 |
3 | 检查A账户是否有1元 |
4 | A的账户扣减1元 |
5 | 给B的账户加1元 |
6 | 解锁B的账户 |
7 | 解锁A的账户 |
以上过程在代码层面甚至可以简化到在一个事物中执行两条sql语句。
分布式环境下事务
和单机事务不同,A、B账户可能不在同一个DB中,此时无法像在单机情况下使用事物来实现。此时可以通过一下方式实现,将转账操作分成两个操作。
A账户
步骤 | 动作 |
---|---|
1 | 锁定A的账户 |
2 | 检查A账户是否有1元 |
3 | A的账户扣减1元 |
4 | 解锁A的账户 |
MQ消息
A账户数据发生变化时,发送MQ消息,MQ服务器将消息推送给转账系统,转账系统来给B账号加钱。
B账户
步骤 | 动作 |
---|---|
1 | 锁定B的账户 |
2 | 给B的账户加1元 |
3 | 解锁B的账户 |
![640?wx_fmt=png](https://img-blog.csdnimg.cn/img_convert/198886b2836deda2039348b8053b9bc5.png)
今晚可以不加班!
![undefined 640?wx_fmt=jpeg](https://img-blog.csdnimg.cn/img_convert/ebf0b6806f3ab135990a1bef71f83381.png)