消息队列高手课
李玥
美团高级技术专家
52199 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 42 讲
进阶篇 (21讲)
消息队列高手课
15
15
1.0x
00:00/00:00
登录|注册

04 | 如何利用事务消息实现分布式事务?

事务反查机制
事务消息
TCC
2PC
持久性
隔离性
一致性
原子性
RocketMQ
分布式系统中的事务实现
ACID特性
消息队列中的“事务”解决消息生产者和消息消费者的数据一致性问题
思考题
消息队列实现分布式事务
分布式事务
事务消息
如何利用事务消息实现分布式事务

该思维导图由 AI 生成,仅供参考

你好,我是李玥,今天我们来聊一聊消息和事务。
一说起事务,你可能自然会联想到数据库。的确,我们日常使用事务的场景,绝大部分都是在操作数据库的时候。像 MySQL、Oracle 这些主流的关系型数据库,也都提供了完整的事务实现。那消息队列为什么也需要事务呢?
其实很多场景下,我们“发消息”这个过程,目的往往是通知另外一个系统或者模块去更新数据,消息队列中的“事务”,主要解决的是消息生产者和消息消费者的数据一致性问题。
依然拿我们熟悉的电商来举个例子。一般来说,用户在电商 APP 上购物时,先把商品加到购物车里,然后几件商品一起下单,最后支付,完成购物流程,就可以愉快地等待收货了。
这个过程中有一个需要用到消息队列的步骤,订单系统创建订单后,发消息给购物车系统,将已下单的商品从购物车中删除。因为从购物车删除已下单商品这个步骤,并不是用户下单支付这个主要流程中必需的步骤,使用消息队列来异步清理购物车是更加合理的设计。
对于订单系统来说,它创建订单的过程中实际上执行了 2 个步骤的操作:
在订单库中插入一条订单数据,创建订单;
发消息给消息队列,消息的内容就是刚刚创建的订单。
购物车系统订阅相应的主题,接收订单创建的消息,然后清理购物车,在购物车中删除订单中的商品。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

消息队列的事务功能在分布式系统中的实现方式是如何的呢?本文通过介绍了分布式事务的概念和消息队列在实现分布式事务中的作用,以及RocketMQ中的分布式事务实现机制。在分布式系统中,数据一致性的保障是非常困难的,因此出现了各种不完整的一致性解决方案。消息队列的事务功能主要解决消息生产者和消费者数据一致性的问题,适用于需要异步更新数据且对数据实时性要求不高的场景。在RocketMQ中,实现了事务反查机制来解决事务消息提交失败的问题,通过定期反查本地事务状态,确保事务的完整性。整体来看,消息队列的事务功能为分布式系统提供了一种相对简单且可靠的分布式事务解决方案。 通过一个订单购物车的例子,我们学习了事务的ACID四个特性,以及如何使用消息队列来实现分布式事务。现有的几种分布式事务的解决方案都不能解决分布式系统中的所有问题,每一种方案都有局限性和特定的适用场景。最后,我们一起学习了RocketMQ的事务反查机制,这种机制通过定期反查事务状态,来补偿提交事务消息可能出现的通信失败。在Kafka的事务功能中,并没有类似的反查机制,需要用户自行去解决这个问题。但是,这不代表RocketMQ的事务功能比Kafka更好,只能说在我们这个例子的场景下,更适合使用RocketMQ。Kafka对于事务的定义、实现和适用场景,和RocketMQ有比较大的差异。 总的来说,本文通过介绍消息队列的事务功能在分布式系统中的实现方式,以及RocketMQ中的分布式事务实现机制,为读者提供了对分布式事务解决方案的全面了解。同时,通过具体案例和比较分析,帮助读者更好地理解了不同分布式事务解决方案的特点和适用场景。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《消息队列高手课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(185)

  • 最新
  • 精选
  • 对于上面订单的例子,为什么不等待订单创建成功再向消息队列发送订单数据呢?这样串行的话,对性能影响应该也不大,也不用考虑订单创建失败而发送消息的情况了。

    作者回复: 考虑这样一种情况:订单创建成功了,还没来得及发消息,这个节点突然断电了。

    2019-07-30
    44
    208
  • 微微一笑
    老师您好,下面是我对思考题的一些看法: A:本地事物的操作1,与往消息队列中生产消息的操作2,是两个分离的操作,不符合对原子性的定义; C:由于操作消息队列属于异步操作,在数据一致性上,只能保证数据的最终一致性。若对于时效性要求很高的系统来说,事物消息不是数据一致的;但对于时效性要求不高的系统来说,他就是数据一致的。我认为,用不同的业务视角来看问题,会有不同的答案; I:隔离性上,由于事物消息是分两步操作的,本地事物提交后,别的事物消息就已经可以看到提交的消息了。所以,不符合隔离性的定义; D:持久性上,rocketMq上支持事物的反查机制,但我不太清楚“半消息”是存储在磁盘中,还是内存里。若存储在磁盘中,那就支持持久性,即使事物消息提交后,发生服务突然宕机也不受影响;若存储在内存中,则无法保证持久性。 以上是我的理解,望老师指点~

    作者回复: 这个总结非常到位,给你点赞!

    2019-07-30
    11
    140
  • ly
    实现订单下单场景: 1. 首先通过producer.sendMessageInTransaction()方法发送一个半消息给MQ. 2. 此时会在TransactionListener中的executeLocalTransaction()方法阻塞,然后在这个方法里面进行订单创建并提交本地事务,如果commit成功,则返回COMMIT状态,否则是ROLLBACK状态,如果正常返回COMMIT或者ROLLBACK的话,不会存在第3步的反查情况。 3. 如果上面的本地事务提交成功以后,此节点突然断电,那么checkLocalTransaction()反查方法就会在某个时候被MQ调用,此方法会根据消息中的订单号去数据库确认订单是否存在,存在就返回COMMIT状态,否则是ROLLBACK状态。 4. 购物车在另外一个项目中,反正只要收到MQ的消息就将本次订单的商品从购物车中删除即可。 以上是通过代码的进行步骤写的,老师看有没有什么问题。

    作者回复: 非常好,完全正确!

    2019-07-30
    13
    80
  • Calix
    这个半消息,和生活中的“交定金”有点类似。

    作者回复: 其实是交全款,不发货。

    2019-08-06
    4
    49
  • 君莫笑
    老师,我回头重新看的时候看到这一章有一点疑问,消息队列的手动确认模式是可以保证分布式事务的最终一致性,那么如果生产者在处理完自己的业务之后将消息放入消息队列中(通过生产者确认方式可以确保消息送达Broker),然后消费者消费这个消息的时候出了问题,假设是消息体本身的原因导致消费该消息一定会抛出异常,这种情况下怎么通知生产者回滚该消息所处理的业务数据呢?

    作者回复: 这种情况下是没有办法回滚的,也不应该回滚。 因为对于消息队列来说,它的一个重要功能就是解耦。 消费者的任何行为,不应该影响生产者。 对于你说的“坏消息”,反复消费都不能成功,有的MQ会把这种消息放到一个单独的特殊队列中,等着后续人工处理,避免卡死队列。

    2019-08-26
    9
    37
  • oscarwin
    先开启本地事务,然后创建订单,订单创建成功后再发消息,根据发消息是否成功来决定提交还是回滚本地事务。这样不需要事务消息也能解决这个场景的问题了?还是说我考虑的不够全面。

    作者回复: 如果本地事务提交失败,已经发出去的消息是无法撤回的,会导致数据不一致。

    2019-07-31
    16
    35
  • Geek
    老师,有几个问题没有太理解,可以解答一下么? 1.kafka在commit/rollback的时候如果发送失败了就会抛出异常,会不会存在已经发送成功了但是超时了的情况呢,这个时候broker已经收到数据了。但是上游业务却回滚了 2.RocketMq反查时有没有可能本地事务还没提交呢,导致broker取消了事务造成了不一致 3.RocketMq在反查时如果订单服务异常了,导致broker取消了会不会导致事务造成了不一致 谢谢老师

    作者回复: 第一个问题,我们后面还有专门的一节课来讲事务是如何实现的,这里面会有你想要的答案。 第二第三个问题,RocketMQ给出的解决方案是,反查的结果返回的状态中,不仅有成功和失败,还有一个“不确定”的状态,意思就是“我现在不知道本地事务是不是成功了,将来它可能会成功,也可能会失败”,像你提的这两种情况,在实现反查接口的时候,都应该返回不确定的状态,RocketMQ在收到这个状态后,会定时多次进行反查,直到得到成功、失败的状态或者事务超时才结束。

    2019-08-21
    2
    32
  • linqw
    使用rocketmq实现分布式事务的理解和疑问,老师有空帮忙解答下哦 1、rocketmq实现分布式事务,使用的是两阶段提交,和mysql写redo log和binlog日志的两阶段提交类似,以上面订单的为例,提交订单消息到mq中,等待mq回复ack,消息提交成功,但是此时的消息对消费组不可见,即half消息,此阶段像mysql的引擎层写redo log的prepare阶段,执行本地事务,执行本地事务成功,此阶段像mysql的service层写binlog的阶段,写binlog成功,最后提交或者回滚队列事务,rocketmq为了防止commit和rollback超时或者失败,采取回查的补偿机制,回查次数默认15次(感觉这个会不会导致服务超时了),超过会rollback,有点像mysql宕机重启根据redo log中的xid找binlog的xid事务,如果binlog日志也已经写成功,mysql这个事务也会提交,因为redo log和binlog这个事务都写完整。 2、消息对消费者不可见,将其消息的主题topic和队列id修改为half topic,原先的主题和队列id也做为消息的属性,如果事务提交或者回滚会将其消息的队列改为原先的队列。rocketMq开启任务,从half topic中获取消息,调用其中的生产者的监听进行回查是否提交回滚。 3、rocketmq采用commitlog存放消息,消费者使用consumeQueue二级索引从commitlog获取消息实体内容,不太理解Index File:索引文件?回查借助OP topic进行获取到Half消息进行后续的回查操作,感觉整体流程还是没有串通,老师能否帮忙解答下么?

    作者回复: indexFile的作用就是给commitlog做的索引,提升读取消息时的查询效率。 另外,关于事务的实现流程,总结的很到位,你还有哪些具体的问题不清楚,可以继续留言提出来。

    2019-07-30
    9
    28
  • Yize Li
    看了之前的一个留言 认为本地数据库和消息系统是两个系统所以违反了原子性 我是有些疑惑的。 我认为 原子性破坏与否取决于是否存在数据库中订单成功但是在购物车中商品没有取消的情况 通过rocketmq的半消息模式是可以保证该情况不出现。所以原子性没有破坏 但是由于消息系统的异步性 导致我们可以观察到事物执行过程中或回滚中的中间状态 这意味着强一致性被破坏 只剩下了最终一致性

    作者回复: 是这样的。

    2019-09-22
    5
    27
  • yan
    如果订单ID是要创建完订单才会有的,那消息中就没有订单ID,那反查本地事务要根据什么查?

    作者回复: 所以几乎所有的类似系统都会事先生成订单ID,而不是在插入数据库的时候才生成。

    2019-09-26
    3
    19
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部