内容简介:在分布式时代,分库分表是很常见的,微服务系统中,各个系统通常使用独立的数据库,所以,事务很难靠数据库本身保证,只能靠业务系统来解决。例如支付宝中的余额宝、花呗,具体不清楚,但猜测应该就是2个服务,不是同一个数据库,我们还花呗的时候通常都是从余额宝中扣除的,这就是分布式事务,一个系统中扣减钱,一个系统中增加钱。下面我们分析下最终一致性的实现方案,最终一致性通常都是使用消息中间件来实现的,系统结构如下:
在分布式时代,分库分表是很常见的,微服务系统中,各个系统通常使用独立的数据库,所以,事务很难靠数据库本身保证,只能靠业务系统来解决。
例如支付宝中的余额宝、花呗,具体不清楚,但猜测应该就是2个服务,不是同一个数据库,我们还花呗的时候通常都是从余额宝中扣除的,这就是分布式事务,一个系统中扣减钱,一个系统中增加钱。
下面我们分析下最终一致性的实现方案,最终一致性通常都是使用消息中间件来实现的,系统结构如下:
用户向系统A发起转账请求,A先在自己的数据库中扣钱,然后通过消息中间件告诉B应该加钱,B收到后在自己的数据库中加钱。
这里有个关键问题,A 更新数据库 和 给消息中间件发消息 是2个操作,如下两个场景怎么处理:
-
先更新数据库,成功了,但发送消息失败了,重发多次还是失败
-
先发消息,成功了,但数据库更新失败,消息撤不回来了
都是因为这2个操作不是原子的,发做谁都有问题。
那看下这样做是否可以,就是把 更新数据库 和 给消息中间件发消息 放到 一个事务 中,这样不就原子了吗?
有问题,例如:
-
如果消息发送失败,具体问题出在哪儿?是消息中间件根本就没收到消息,还是收到消息后response时出错了?如果是根本没收到还好一点,如果是收到了但响应失败就麻烦了,导致A数据库回滚,没有扣钱,但B收到消息了,加钱了。
-
如果发消息时网络延迟很高怎么办,数据库事务一直被拖着,性能差,风险高。
所以, 放入一个事务中 这种方法是 不可取 的。
为了保证原子性,可以变通一下, 添加一个消息表 ,A不直接往消息中间件中发消息,而是把消息写入消息表,然后通过一个后台程序不断的把消息写入消息中间件。
这个后台程序源源不断的把消息表中的消息发到消息中间件,如果失败就重试,可以保证:
-
消息不会丢失
-
顺序不乱
但会有消息重复的情况,因为消息发送失败可能是写入失败,也可能是写入成功但响应失败,所以消息可能会重复,这个问题需要系统B来处理。
系统B需要考虑2个问题:
-
消息丢失
B从消息中间件中拿到消息,还没处理完就宕机了,这条消息怎么办?
需要通过ACK机制处理,消费成功的发送ACK,对于没有ACK的消息,消息中间件会再次推送。
-
消息重复
ACK机制也存在消息重复的情况,比如B已经处理完一条消息,发ACK时失败了,那么这条消息就还会被推过来。
还有就是上面说的后台程序发消息时可能重复。
对于重复消息问题,可以加一个 判重表 ,记录处理成功的消息,每次收到消息时,先通过判重表判断一下,如果重复了就不处理,实现幂等性。
这样,整体结构就变为:
以上就是通过最终一致性解决分布式事务问题的基本思路,A 保证消息不丢,B 保证消息不漏、幂等。
> 内容整理自《软件架构设计》
点击:point_down: 阅读原文 查看 文章列表
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hit Refresh
Satya Nadella、Greg Shaw / HarperBusiness / 2017-9-26 / USD 20.37
Hit Refresh is about individual change, about the transformation happening inside of Microsoft and the technology that will soon impact all of our lives—the arrival of the most exciting and disruptive......一起来看看 《Hit Refresh》 这本书的介绍吧!