码迷,mamicode.com
首页 > 其他好文 > 详细

分布式事务原理解析

时间:2019-08-15 19:29:41      阅读:99      评论:0      收藏:0      [点我收藏+]

标签:幂等性   form   表示   疑问   流程   一段   不同   订阅   队列   

1. 分布式事务原理解析

1.1. TCC分布式事务

了解过TCC分布式事务的都知道它有三个阶段:try,confirm,cancel,但很多文章就只有原理图,和对原理图的解释,看一遍也留不下印象,这里用实际场景举个例子,说明TCC分布式事务原理

  • try阶段:假设我们又订单系统,它需要调用库存和积分系统,try阶段我们进行的是预处理,比如下单1个商品,在try操作中,我们在库存表设置个冻结字段,表示冻结1个商品,商品的存量先不扣除,而积分表同样添加个预增加积分字段,添加个预积分比如10
  • confirm阶段:我们为什么要经历try阶段?,为了尽可能的保证各个系统都是正常工作的,数据库,服务都没有挂掉,资源没有不足,则可以最大程度上保证confirm阶段能正确执行,confirm阶段也就是正式的扣除库存和增加积分
  • cancel阶段:若try阶段执行错误,则会对前面已经执行的try阶段的系统执行cancel操作,也就是反向SQL回滚,冻结的商品-1,预积分-10。到这里有没有疑问?我首先想到的是若confirm或cancel操作再执行失败怎么办?这里就要由TCC分布式事务框架保证了,它会记录事务活动日志,再confirm或cancel失败后不断尝试调用confirm和cancel的逻辑,所以这里需要开发者自己保证,你的SQL是正确的

TCC分布式框架推荐:ByteTCC,tcc-transaction,himly

1.2. 最终一致性分布式事务

1.2.1. 原理

技术图片

最终一致性方案一般都是有消息中间件来完成的,核心流程如上图所示

  • 假设上游服务为服务A,可靠消息服务为服务B,下游服务为服务C,首选A发送请求给B表示我将要发消息了,你准备下,然后B记录下待确认的数据;
  • A服务正式走完本地数据库逻辑,在发送B确认消息,说我执行完了,你可以确认了,然后B就更新消息的状态为已发送,并把消息发送给MQ
  • 此时服务C订阅了MQ,接收到B通过MQ发送过来的消息,并执行本地数据库操作,在执行完毕后手动ack确认消费完毕,这就走完了全部流程

1.2.2. 问题

下面来考虑这中间会有什么问题了,为什么这样能保证分布式事务的最终一致性?

  • 首先是服务A调用B过程中若不成功或者服务B若没保存待确认消息,那就直接返回的就是失败,还没有操作数据库,所以没有影响
  • A操作数据库和发送确认消息,我们需要放在同一个本地事务中,确保同时成功或失败,这样成功了当然没问题,失败了呢?因为B服务的数据库已经存在待确认消息,可以在B服务开条线程定时判断待确认消息,若发现待确认消息很久没被确认,则主动向A发起请求,判断该操作是否成功了?成功则改状态为已确认,继续执行,失败则删掉该记录
  • B到MQ的过程,若是失败了,MQ挂了之类的,我们可以在B服务后台起个线程,定时判断已确认的消息,在一定时间后是否变成已发送,没有发送的再主动发送
  • 这样后就只剩MQ到C服务了,MQ有重试机制,所以只要业务逻辑没问题,就可以保证最终一致性(这个过程中需要保证MQ到C服务,接口方法需要幂等性)
  • 上述流程需要特别注意的一个点就是MQ,我们需要保证MQ的高可用,否则一旦全部MQ宕机,依赖MQ的分布式事务都不能完成

1.2.3. MQ挂了怎么办

越大的公司,考虑的就越多,任何组件都可能挂掉,MQ如果就一个集群,就要考虑这个集群压力过大到爆掉了怎么办?资金雄厚并发压力大的公司可以直接搞再搞一套备用的,当MQ请求不通后,立即自动切换到备用MQ集群,当然这肯定会造成资源的浪费,毕竟要再搞一套MQ不运行一直放那里,这里再给出一套参考方案(如果你们有redis集群的话)

  • redis有种队列的数据结构,它是可以用来临时充当消息队列的,所以这里要讨论的是,如果用redis当备用方案需要考虑什么问题?
  • 通知机制:当MQ挂了,我们需要将所有用到MQ事务的系统都切换到备用redis方案,所以我们要有个通知机制,本来吗这个类似广播模式的通知应该是MQ完成的,但没了MQ,我们要有其它方案,比如zookeeper的watch机制,zk有监听机制可以通知监听它节点的系统,打开降级开关,达到通知其它系统的效果
  • 消息读取热点问题:我们把消息放到redis的队列会有个对应的key,我们不能把所有消息都放一个key的value中,这样会导致这个value数据量过大;所以这里给出方案:划分上百个队列,对消息hash后放入这些队列,这样尽可能的把消息分散到不同的key
  • 故障的恢复:当我们把MQ恢复过来时,我们也要通知系统,该切换回来了,这个该怎么做呢?可以开启一个线程,每隔一段时间尝试给MQ投递一个消息查看是否恢复,若恢复了,就可以再次通过zookeeper来关闭降级开关

参考:
拜托,面试请不要再问我TCC分布式事务的实现原理!
最终一致性分布式事务如何保障实际生产中99.99%高可用?

分布式事务原理解析

标签:幂等性   form   表示   疑问   流程   一段   不同   订阅   队列   

原文地址:https://www.cnblogs.com/sky-chen/p/11359634.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!