一次给女朋友转账引发我对分布式事务的思考

  • 时间:
  • 浏览:108
  • 来源:我爱生活网 - 专注共享客串博客资源

   分布式事务也不我在分布式的场景下,前要满足事务的需求!上篇文章亲们聊过了消息上面件,那这篇文章亲们要聊的是分布式事务,把两者一结合,便有了基于消息上面件的分布式事务补救方案!不管是本地事务,还是分布式事务,也有为了补救数据的一致性大大问题!一致性你你这种词咱们前面多次提及!与本地事务不同的是,分布式事务前要保证的是分布式环境下,不同数据库表中的数据的一致性大大问题。分布式事务的补救方案有多种,如XA协议、TCC三阶段提交、基于消息队列等等,本文只会涉及基于消息队列的补救方案!

   通过上面的图亲们能就看,引入消息队列后,系统的复杂瞬间提升了,之很多很多很多很多有弥补了亲们第一种方案的几个匮乏点,但也带来了更多的大大问题,比如消息队列系统一种的可用性、消息队列的延迟等等!或者,没有 的设计依然没有 补救亲们面临的核心大大问题-数据的一致性

   回到学生时代老师给亲们举的经典栗子,A账户给B账户转账80元(A、B位于同有有好几个 库中),将会A的账户位于扣款,B的账户却没有 到账,这就总出 了数据的不一致!为了保证数据的一致性,数据库的事务机制会让A账户扣款和B在账户到账的有有好几个 操作要么一起成功,将会有有有好几个 操作失败,则多个操作一起回滚,这也不我事务的原子性,为了保证事务操作的原子性,就前要实现基于日志的REDO/UNDO机制!或者,仅有原子性还匮乏,将会亲们的系统是运行在tcp连接环境下,将会多个事务并行,即使保证了每有有好几个 事务的原子性,仍然会总出 数据不一致的状态。之类A账户没有 有80元的余额, A账户给B账户转账80元,先读取A账户的余额,或者在你你这种值上减去80元,或者在这有有好几个 操作之间,A账户又给C账户转账80元,没有 最后的结果应该是A减去了80元。但事实上,A账户给B账户最终完成转账后,A账户只减掉了80元,将会A账户向C账户转账减掉的80元被覆盖了!很多很多很多很多有为了保证并发状态下的一致性,又引入的隔离性,即多个事务并发执行后的状态,和它们串行执行后的状态是等价的!隔离性又有多种隔离级别,为了实现隔离性(最终也有为了保证一致性)数据库又引入了悲观锁、乐观锁等等……本文的主题是分布式事务,很多很多很多很多有本地事务就也不我简单回顾一下,前要记住的有些是,事务是为了保证数据的一致性

   为何从我卡里扣钱没有 迅速,而对方却要几秒不能到账?或者转账失败后,扣除的钱还能及时的返还到我的卡里?万一钱返还失败为何办?又将会我转一次钱,对方却收到了两次转账的申请又该要怎样?带着什么大大问题,我脑海中浮现出“事务”二字!

   当然,分布式事务最好的补救方案是尽量补救总出 分布式事务!

1、将会“第1步”刚执行完,系统将会一种因为 宕机了,那会因为 A银行账户扣款了,或者写入消息队列失败,无法进行B银行接口调用,从而因为 数据不一致。

2、将会B银行在执行“第5步”时将会校验失败而未能成功转账,在回调A银行接口通知回滚时网络异常将会宕机,会因为 A银行转账无法完成回滚,从而因为 数据不一致。

   刚刚都将会从我卡里扣过钱了,现在却提示我转账失败,银行会不想把我的钱给吞了?转账失败的钱还能退换给我吗?正在我紧张、焦虑、坐立不安之时又收到根小app冲正的消息,刚刚转账失败的钱将会撤出 给我了,看来我多虑了……这也证明咱平安银行的app还是比较安全靠谱的!

本文在被委托人技术博客不同步发布,详情可用力戳

亦可扫描屏幕右侧二维码关注被委托人公众号,公众号内有被委托人联系土依据,等你来撩...

  

1、同步调用远程接口,将会接口比较耗时,会因为 主tcp连接阻塞时间较长。

2、流量没有 很好控制,A银行系统的流量高峰将会压垮B银行系统(当然B银行肯定会有被委托人的限流机制)。

3、将会“第1步”刚执行完,系统将会一种因为 宕机了,那会因为 A银行账户扣款了,或者B银行没有 收到接口的调用,这就总出 了有有好几个 系统数据的不一致。

4、将会在执行“第3步”后,B银行将会一种因为 宕机了而无法正确组阁 请求(实际上转账操作在B银行系统将会执行且入库),这刚刚A银行在等待接口响应会异常,误以为转账失败而回滚“第1步”操作,这也会总出 了有有好几个 系统数据的不一致。

  CAP理论是由加州大学Eric Brewer教授提出来的,你你这种理论真不知道们,有有好几个 分布式系统不将会一起满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)这有有好几个 基本需求,最多没有 一起满足其中两项。

  一致性:这里的一致性是指数据的强一致,也称为线性一致性。是位于分布式环境中,数据在多个副本之间与否不能保持一致的型态。也也不我说对某个数据进行写操作后立马执行读操作,前不能读取到刚刚写入的值。(any read operation that begins after a write operation completes must return that value, or the result of a later write operation)

  可用性:任意被无故障节点接收到的请求,前要不能在有限的时间内响应结果。(every request received by a non-failing node in the system must result in a response)

  分区容错性:将会集群中的机器被分成了两部分,这两部分没有 互相通信,系统与否能继续正常工作。(the network will be allowed to lose arbitrarily many messages sent from one node to another)

   对于大大问题的1、2都很好补救,将会对消息队列熟悉的亲们应该调快能想到可否 引入消息上面件进行异步和削峰补救,于是又重新设计了有有好几个 方案,流程如下:

   前两天发了工资,第一反应是想着要给远方的女亲们有些惊喜!于是打开了平安银行的APP给女亲们转点钱!填写上对方招商银行卡的卡号、开户名,一键转账!追到!在我点击的那瞬间,就收到了app的账户变动的提醒,或者总出 了图一所示的提示界面:“补救中,正在在等待对方银行返回结果…”。嗯!毕竟是跨行转账嘛,等个几秒也正常!脑海刚始于浮现出女亲们收到转账后惊喜与感动的画面!

1、A银行对转出账户执行检查校验,进行金额扣减。

2、A银行同步调用B银行转账接口。

3、B银行对转入账户进行检查校验,进行金额增加。

4、B银行返回补救结果给A银行。

  

   面对上述大大问题,亲们不得不对系统再次进行升级改造。为了补救“A银行账户扣款了,或者写入消息队列失败”的大大问题,亲们前要借助有有好几个 转账日志表,将会叫转账流水表,该表简单的设计如下:

  还记得刚毕业那年,带着满腔的热血就去到了一家互联网公司,领导给我的第有有好几个 任务也不我在列表上增加有有好几个 修改数据的功能。这能难倒我?我分分钟给你搞出来!不也不我在列表上增加了有有好几个 “修改”按钮,点击按钮弹出框修改后保存就好了么。然而一切不像帮我 象的没有 顺利,点击保存并刷新列表后,页面上的数据还是显示的修改刚刚的内容,像没有 修改成功一样!过一会儿再刷新列表,数据就能正常显示了!测试多次刚刚也有没有 !没见过什么大场面的我刚始于不得劲慌了,是我哪里写得不对么?最终,我不得不求助组内经验比较充沛的前辈!他深吸了一口气告诉是我不好:“毕竟是刚毕业的小伙子啊!我来跟你讲讲因为 吧!亲们的数据库是做了读写分离的,部分读库与写库在不同的网络分区。你的数据更新到了写库,而读数据的刚刚是从读库读取的。更新到写库的数据同步到读库是有一定的延迟的,也也不我说读库与写库会有短暂的数据不一致”! “没有 不想体验不好么?为何没有 做到写入的数据立马能读出来?没有 你你这种功能该为何实现呢?” 面对我的一堆大大问题,同事有些不耐烦的说:“听说过CAP理论吗?你先被委托人去了解一下吧”!是我刚始于查阅各种资料去了解你你这种陌生的词身旁的秘密!

   谈到本地事务,亲们将会都不粉悉,将会你你这种数据库引擎层面能支持的!很多很多很多很多有也称数据库事务,数据库事务四大型态:原子性(A),一致性(C),隔离性(I)和持久性(D),而在这四大型态中,我认为一致性是最基本的型态,其它的有有好几个 型态都为了保证一致性而位于的!

   到目前为止,亲们很好的补救了消息丢失的大大问题,保证了也不我A银行转账操作成功,转账的请求就一定能发送到B银行!或者该方案又引入了有有好几个 大大问题,通刚刚台tcp连接轮询将消息装进去 消息队列补救,同一次转账请求将会会总出 多次装进去 消息队列而多次消费的状态,没有 B银行会对同一转账多次补救因为 数据总出 不一致!那为何保证B银行转账接口的幂等性呢?

  

   在亲们还在“牙牙学语”的刚刚,老师老是会通过转账的栗子来跟亲们讲解事务,但跟这里场景不一样的是,老师讲的是本地事务,而这上面对的是分布式事务!亲们先来简单回顾一下本地事务!

  在分布式系统中,分区容错性是基本要保证的。也也不我说没有 在一致性和可用性之间进行选着。一致性和可用性,为何不将会一起成立?回到刚刚修改列表的例子,将会数据会分布在不同的网络分区,必然会位于数据同步的大大问题,而同步会位于网络延迟、异常等大大问题,很多很多很多很多有会总出 数据的不一致!将会要保证数据的一致性,没有 就前要在对写库进行操作时,锁定有些读库的操作。没有 写入成功且完成数据同步后,不能重新放开读写,而没有 在锁定期间,系统丧失了可用性。更完整性关于CAP理论可否 参考这篇文章,该文章讲得比较通俗易懂!

   你你这种流水表前要为何用呢?亲们在“第1步”进行扣款时,一起往流水表写入根小操作流水,状态为“待补救”,或者这有有好几个 操作前也不我原子的,也也不我说前要通过本地事务保证这有有好几个 操作要么一起成功,要么一起失败!这就保证了也不我转账扣款成功,必定会记录根小状态为“待补救”的转账流水。将会在你你这种步失败了,那自然也不我转账失败,没有 后续操作了。将会这步操作后系统宕机了因为 没有 将消息成功写入消息队列(也也不我“第2步”)也没关系,将会亲们的流水数据将会持久化了!这刚刚亲们只前要加入有有好几个 后台tcp连接进行补偿,定期的从转账流水表中读取状态为“待补救”且最后更新的时间距当前时间大于某个阈值的数据,重新装进去 消息队列进行补偿。没有 ,就保证了消息即使丢失,也会有补偿机制!B银行在补救完转账请求也有回调A银行的接口通知转账的状态,从而更新A银行流水表中的状态字段!没有 就完美补救了上有有好几个 方案中的有有好几个 匮乏点。系统设计图如下:

  

   然而,一切并没有 没有 顺利,刚过一会儿,app却如图二所示的提示我“将会收款人户名不符”因为 转账失败!!!

   在正常状态对一致性要求不高的场景,没有 的设计是可否 满足需求的。或者像银行没有 的系统,将会没有 实现要花费早就破产了吧。亲们先看看没有 的设计最主要的大大问题:

1、A银行对账户进行检查校验,进行金额扣减。

2、将对B银行的请求异步写入队列,主tcp连接返回。

3、启动后台tcp连接从队列获取待补救数据。

4、后台tcp连接对B银行接口进行远程调用。

5、B银行对转入账户进行检查校验,进行金额增加。

6、B银行补救完成回调A银行接口通知补救结果。

   同样的,亲们可否 在B银行系统中前要增加有有好几个 转账日志表,将会叫转账流水表,B银行每次接收到转账请求,在对账户进行操作的刚刚一起往转账日志表中插入根小转账日志记录,同样这有有好几个 操作也前也不我原子的!在接收到转账请求后,首先根据唯一转账流水Id在日志表中查找判断该转账与否将会补救过,将会未补救过则进行补救,或者直接回调返回! 最终的架构图如下:

  

  

   很多很多很多很多有,亲们这里最核心的也不我A银行通过本地事务保证日志记录+后台tcp连接轮询保证消息不丢失。B银行通过本地事务保证日志记录从而保证消息不重复消费!B银行在回调A银行的接口也有通知补救结果,将会转账失败,A银行会根据补救结果进行回滚。

   本地事务讲到了一致性,分布式事务不可补救的面临着一致性的大大问题!回到最刚始于跨行转账的例子,将会A银行用户向B银行用户转账,正常流程应该是: