一、前言

在分布式、微服务大行其道的今天,相信大家对这些词都不陌生。拆分微服务的好处如下:

1、每个人只需要维护自己单独的服务,没有了以前的各种代码冲突

2、自己想测试、想发布、想升级,只需要care自己写的代码就ok,很方便,很贴心。

任何技术的引用,都会带来新的问题。今天就谈一谈分布式系统中最棘手的问题:分布式事务

二、神马是事务

事务就是一系列操作,要么同时成功,要么同时失败。然后从事务的ACID展开叙述。那么为啥要有这个概念呢?是为了解决什么问题?

最经典的例子,莫过于银行转账。我们要从A账户转1000到B账户。正常情况下,我们从A转出1000到B账户之后,A账户余额减1000(这个操作,我们用action1代表),B账户余额加1000(这个操作,我们用action2代表)。action1和action2是两个操作。既然是两个操作就一定会存在执行的先后顺序。那么可能会出现action1执行完,刚准备去执行action2的时候出问题了。(比如数据库负载过大,暂时拒绝访问)。类比到生活中就是,我给朋友转了1000块,然后我卡里的余额少了1000,但是我朋友没收到钱。

为了解决“钱到底去哪里了”的问题,引入了事务的概念。也就是说,既然我转账的时候你保证不了100%能成功,比如银行系统只能保证99.99%的高可用,那么在那0.01%的时间里如果出现了上述问题,银行系统直接回滚action1操作?(即把1000块钱再加回余额中去)

对于银行系统来说,可能在0.01%的时间里我保证不了action1和action2同时成功,那么在出问题的时候,我保证它俩同时失败。(事务的原子性)

三、java中的事务

搞懂了事务是怎么来的,我们来看点眼熟的,java中的事务是怎么玩的?

java中,我们平时用的最多的就是在service层的增删改查方法上添加@Transactional注解。让spring去帮我们管理事务。

它的底层会给我们的service组件生成一个对应的proxy动态代理,这样所有对service组件的方法都由它对应的proxy来接管。

当proxy在调用对应业务方法比如add()时,proxy就会基于AOP的思想在调用真正的业务方法前执行setAutoCommit(false)打开事务。

然后在业务方法执行完后执行commit提交事务,当在执行业务方法的过程中发生异常时就会执行rollback来回滚事务。

当然@Transactional注解具体的实现细节这里不再展开,这个不是本篇文章的重点,本文的topic是“分布式事务”,关于@Transactional注解大家有兴趣的话,可以自己研究下。

四、分布式事务

终于来到了重点。既然有了事务,并且使用spring的@Transactional注解来控制事务是如此的方便。那为啥还要搞一个分布式事务的概念出来呢?更近一步,分布式事务和普通事务到底是啥关系?有什么区别?分布式事务又是为了解决什么问题出现的?

简单来说,就是分布式系统中的事务,至于什么是分布式系统,可以参考石衫老师的文章。《到底啥是分布式系统开发经验?》

如上图所示,一个单块系统有三个模块:员工模块、财务模块、请假模块。假如现在有1个操作,需要按顺序去调用完成这三个模块中的接口。这个操作是一个整体,包含在1个事务中,要么同时成功,要么同时失败回滚。这是ok的。但当我们把单块系统拆分成分布式系统或者微服务架构,事务就不是上面那么玩了。

拆分成分布式系统之后的图:

上图是同一操作在分布式系统中的执行情况。 员工模块、财务模块、请假模块分别拆分成员工系统、财务系统、请假系统。

比如一个用户进行一个操作,这个操作需要先调用员工系统预先处理一下,然后http或者rpc的方式分别调用财务系统和请假系统的接口做进一步的处理。它们的操作都需要落地到数据库中。这3个系统的一系列操作是一个分布式事务,此时这三个系统的操作,要么同时成功,要么同时失败。@Transactional注解是不行的,因为它只能控制同一个JVM进程中的事务,但是对于这种跨多个JVM进程的事务无能无力。

五、分布式事务的几种实现思想

下面给大家介绍几种常见的分布式事务实现方案。

1、可靠消息最终一致性方案

整个流程图如下所示:

我们来解析一下这个方案的大致流程:

① A系统先发一个prepared消息,如果消费发送失败,直接取消操作,后续都不执行了。

② 如果这个消息发送成功了,执行A系统的本地事务,失败就告诉mq回滚消息,后序都不执行了。

③ 如果A系统本地事务执行成功,告诉mq发送确认消息

④ 那如果A系统迟迟不发确认消息呢?

此时mq会自动轮训所有prepared消息,然后调用A系统事先提供的接口,通过这个接口,反查A系统的上次本地事务是否执行成功。如果成功就发送确认消息给MQ,失败则告诉mq回滚消息。

⑤ 此时B系统接收到确认消息,然后执行本地事务,如果本地事务执行成功则事务正常完成。

⑥ 如果系统B的本地事务执行失败了咋办?

基于MQ重试,MQ会自动不断重试直到成功,实在不行,可以发送报警由人工来手动回滚或补偿。

这种方案的要点就是 可以基于MQ来不断进行重试,最终一定会执行成功。因为一般执行执行失败的原因为网络抖动,或者数据库瞬间负载过高。都是暂时性问题。通过这种方案99.99%的情况都是可以保证数据最终一致的,剩下的0.1%出问题的时候,就人工修复数据。

适用场景:

这个方案的使用还是比较广的,目前国内互联网公司大都是基于这个思路玩的。

2、最大努力通知方案

整个流程如如下所示:

这个方案的大致流程 :

① 系统A本地事务执行完之后,发送个消息到MQ

② 这里会有个专门消费MQ的最大努力通知服务,这个服务会消费MQ,然后写入数据库中记录下来,或者是放入个内存队列。接着调用系统B的接口

③ 假如系统B执行成功就万事ok了,但是如果系统B执行失败了呢?

那么此时最大努力通知服务就定时尝试重新调用系统B,反复N次,最后还是不行就放弃。

这套方案和上面的可靠消息最终一致性方案的区别

可靠消息最终一致性方案可以保证的是只要系统A的事务完成,通过不断(无限次)重试来保证系统B的事务总会完成。

但最大努力方案就不同,如果系统B本地事务执行失败了,那么它会重试N次之后就不再重试,系统B的本地事务可能就不会完成啦。至于你想控制它究竟有多“努力”,这个需要结合自己的业务来配置。

比如在电商系统中,在下完订单后发短信通知用户下单成功的业务场景中,下单正常完成,但是到了发短信的这个环节,由于短信服务暂时有点问题,导致重试了3次还是失败。那么此时就不再重试发送短信了。因为在这个场景中我们认为3次就已经算是尽了“最大努力”了。

简单总结:就是在指定的重试次数内,如果能执行成功那么皆大欢喜,如果超过了最大重试次数就放弃,不再进行重试。

适用场景:

一般用在不太重要的业务场景中,就是那种完成的话是锦上添花,但失败的话对我也没啥坏影响。比如上面提到的电商中的部分通知短信,就比较适合使用这种最大努力通知方案来做分布式事务的保证。

3、TCC强一致性方案

TCC的全称是:

Try(尝试)

Confirm(确认/提交)

Cancel(回滚)

这个其实是用到了补偿的概念,分为3个阶段。

① Try阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留。

② Confirm阶段:这个阶段说的是在各个系统中实际执行的操作。

③ Cancel阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。

给大家举个例子:

比如说跨银行转账的时候,要涉及两个银行的分布式事务,如果用TCC方案来实现,思路是这样的。

1、Try阶段:先把两个银行中的资金给它冻住就不让操作啦。

2、confirm阶段:执行实际的转账操作,A银行账户资金减扣,B银行账户的资金增加。

3、Cancel阶段:如果任何一个银行的操作执行失败,那么就需要回滚进行补偿,就是比如A银行账户如果已经扣减了,但是B银行账户资金增加失败了,那么就得把A银行账户资金给加回去

适用场景:

这种方案说实话几乎很少有人使用,我们用的也比较少,但是也有使用的场景。因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大,非常之恶心。比如说我们,一般来说跟钱相关的,跟钱打交道的,支付、交易相关的场景,我们会用TCC,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,在资金上不允许出现问题。

比较适合的场景:除非你是真的一致性要求太高,是你系统中核心之核心的场景,比如常见的就是资金类的场景,那你可以用TCC方案了。你需要自己编写大量的业务逻辑,自己判断一个事务中的各个环节是否ok,不ok就执行补偿/回滚代码。而且最好是你的各个业务执行的时间都比较短。但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码很难维护。

六、学员提问的问题

我们想保证某个系统非常的可靠,任何一个数据都不能错。我们用的是微服务架构,几十个服务。结果,我们一盘点,发现,如果到处都要搞的话,一个系统要做几十个分布式事务出来。

我们的经验,我带几十个人的team,最大的一个项目,起码几百个服务。复杂的分布式大型系统,里面其实也没几个分布式事务。

你其实用任何一个分布式事务方案,都会导致你那块代码复杂10倍。很多请求下,系统A调用系统B、系统C、系统D,我们可能根本就不做分布式事务

如果调用出错,就打印异常日志。每个月也就那么几个bug,很多bug是功能性的、体验性的。真正涉及到数据层面的一些bug,一个月可能就两三个。

需要balance一下。如果你为了确保系统保证数据100% 不出错,上了几十个分布式事务。代码太复杂,性能太差,系统吞吐量、 性能大幅度下跌。

99%的分布式接口调用,不要做分布式事务。直接就是监控(发邮件,发短信)、记录日志(一旦出错,完整的日志),事后快速定位,排查和出解决方案,修复数据。

每个月,每隔几个月,都会对少量因为bug导致出错的数据,进行人工的数据修复。自己临时动手写个程序。可能要补一下数据,要删一下数据。或者修改一些字段的值。

比你做50个事务,成本要低得多。

trade off,权衡。要用分布式事务,一定会有成本的,代码会很复杂,开发很长时间,性能和吞吐量下降,系统更加复杂、更加脆弱反而更容易出bug。好处就是如果做好了,TCC,可靠消息最终一致性方案,一定可以100%保证你那块的数据不会出错。

1%,0.1%,0.01%的业务,资金、交易、订单,我们会用分布式事务方案来保证,会员积分、优惠券、商品信息,其实不要这么搞了。

六、总结

本篇介绍了什么是分布式事务,然后还介绍了最常用的3种分布式事务方案。但除了上边的方案外,其实还有两阶段提交方案(XA方案)和本地消息表等方案。但是说实话极少有公司使用这些方案,鉴于篇幅所限,不做介绍。后续如果有机会再出篇文章,详细聊聊这两种方案的思路。

【分布式】聊一聊分布式事务相关推荐

  1. 【分布式事务】tcc-transaction分布式TCC型事务框架搭建与实战案例(基于Dubbo/Dubbox)...

    一.背景 有一定分布式开发经验的朋友都知道,产品/项目/系统最初为了能够快速迭代上线,往往不太注重产品/项目/系统的高可靠性.高性能与高扩展性,采用单体应用和单实例数据库的架构方式快速迭代开发:当产品 ...

  2. 分布式架构 --- 分布式锁

    分布式锁 1. 研究背景及其意义 2. 分布式锁的介绍 2.1 分布式锁 2.2 为什么需要分布式锁 2.3 分布式锁的基本要求 3. 分布式锁的实现 3.1 基于数据库的分布式锁 3.1.1选用数据 ...

  3. 分布式-4分布式集群

    系列文章目录 分布式–1概述CAP和Base 分布式–2分布式事务 分布式–3分布式一致性算法 分布式-4集群 分布式–5服务限流算法 分布式–6分布式id 分布式–7性能压测 分布式–8日志链路跟踪 ...

  4. etcd分布式之分布式通知与协调

    分布式之分布式通知与协调:        通过etcd进行低耦合的心跳检测        通过etcd完成系统调度        通过etcd完成工作汇报

  5. 分布式面试 - 分布式服务接口请求的顺序性如何保证?

    分布式面试 - 分布式服务接口请求的顺序性如何保证? 面试题 分布式服务接口请求的顺序性如何保证? 面试官心理分析 其实分布式系统接口的调用顺序,也是个问题,一般来说是不用保证顺序的.但是有时候可能确 ...

  6. 分布式面试 - 分布式锁的常见问题

    分布式面试 - 分布式锁的常见问题 面试题 一般实现分布式锁都有哪些方式?使用 redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? 面试官心理分析 ...

  7. 什么是分布式,分布式和集群的区别又是什么?

    1. 什么是分布式 ? 分布式系统一定是由多个节点组成的系统. 其中,节点指的是计算机服务器,而且这些节点一般不是孤立的,而是互通的. 这些连通的节点上部署了我们的节点,并且相互的操作会有协同. 分布 ...

  8. 什么是分布式,分布式和集群的区别又是什么?这一篇让你彻底明白!

    1. 什么是分布式 ? 分布式系统一定是由多个节点组成的系统. 其中,节点指的是计算机服务器,而且这些节点一般不是孤立的,而是互通的. 这些连通的节点上部署了我们的节点,并且相互的操作会有协同. 分布 ...

  9. 20210813 数据摆渡、DNS、分布式、分布式数据库、防ddos攻击、链路负载均衡

    数据摆渡 数据摆渡是什么?摆渡的意思是,在没有道路的时候,坐船度过这片水域到达对岸. 数据摆渡顾名思义,是指在没有物理连接的情况下,数据通过隔离部件从源端到达目的端. 再通俗一点,就是通过具有存储介质 ...

  10. 【DDBMS】分布式数据管理——分布式查询优化的探索

    目录 关于分布式数据库系统 分布式数据库出现的原因 了解分布式数据库系统 探索方向 探索过程 探索意义 探索难题 研究现状 关于查询的大致过程 层次结构 过程简述 应用场景实例 功能分析 数据设计 半 ...

最新文章

  1. eclipse注释模板设置(未整理)
  2. 【文件格式问题】文件格式 Windows、Unix/Linux、Mac 导致的问题及处理(idea 或 notepad++ 档案格式转换方法)
  3. java.lang包—类加载器ClassLoader类
  4. 《权力的游戏》第八季剧情翻水:超40万粉丝请愿重拍
  5. autojs 如何获取控件的desc_owchart教程三:如何添加成交量?
  6. cartopy模块介绍与安装
  7. 计算机类专业毕业设计(课程设计)题目大全
  8. Java Web入门之网络聊天室
  9. MATLAB--数字图像处理 车牌识别之分离字符
  10. 计算机在it行业的应用,IT行业的前景怎样啊,我是自考本科毕业,计算机应用...
  11. 漏洞平台之pikachu详细图文搭建教程
  12. c++小游戏(5项)
  13. 本地搭建xxl-job服务及连接验证
  14. 【ABviewer从零开始教学查看器篇①】3D查看器和3D剖面板
  15. 留住前端面试官目光简历到底是怎样?4个方面把你简历问题一一突破
  16. 历届图灵奖 (Turing award)得奖名单
  17. 计算机科学英语 --- 持续更新中(2018/10/18)
  18. 多智能体强化学习(MARL)训练环境总结
  19. 高薪就业面试题系列 人事篇三
  20. 罗技K580键盘快捷键(ipad)

热门文章

  1. *uni-app之H5端跨域问题解决方案
  2. apple邀请码发放(apple邀请码,apple卡密获取)
  3. fatal: unable to auto-detect email address (got ‘tim@newton.(none)‘)
  4. centos上安装和卸载mysql
  5. pt100温度传感器c语言,pt100温度传感器阻值,pt100温度与阻值对照表
  6. 易语言做mysql登录器_易语言制作简单的一个登陆器
  7. 【力扣周赛】第347场周赛
  8. win10搜索计算机中所有excel,win10系统用excel表格查找和替换功能的操作方法
  9. (牛客网)华为机试(二)
  10. 芝加哥大学计算机专业硕士,芝加哥大学-计算机科学硕士