说人话中,实战讲干货。你好,欢迎来到 it 老齐的架构三百讲,我是你们的 it 私人顾问老齐。到今年呢,我从业已经十八年了,一直做扎瓦与架构的研发工作。今年呢是我创业的第一年,目前呢,已经录制了十多门与编程架构相关的课程, 同时我还会提供点对点的简历优化、模拟面试、 offer 选择、解决方案、架构指导等服务。总之呢,只要是我有经验的,能给兄弟们帮上忙的信息呢,我一定坦诚相待。有需要的小伙伴呢,可以看一下评论区或者我的个人描述,希望能用我的经验帮你少走弯路,找到更好的工作。 今天咱们来聊一聊在分布式事物的这种场景下呢,里面包含的 a t t c c saga 还有 x c 这四种方案到底都是什么意思?我们平时在工作中 又该怎么去选呢?好的内容创作不易,希望兄弟们能给点一个免费的赞,谢谢大家了 啊,那咱们回到笔记来进行讲解。首先呢,我们重新回顾一下,为什么要有分布式事物呢?其实原因很简单啊, 我们在进行分布式系统开发的时候,往往系统和系统之间呢,他们底层呢就要产生一些联系,比如说我们在创建订单的时候,就要去扣减库存, 那此时订单创建是在我们订单服务的订单库中,而扣减库存呢,是要在仓储库中来进行扣减, 那因为我们底层是要在两个不同的数据库中来进行数据操作,那是如何保证数据要么全部提交,要么数据全部回滚呢?这里就有很大的学问了,这一讲 好呢,我就以咱们在开源领域非常著名的阿里巴巴开源的 cta 为例呢来讲解,到底它里边所包含的像 attcc, saga 还有 xa 模式到底是怎么回事儿? 那这里呢,我先给大家简单介绍一下。在 set 中呢,它有一个组件叫做 t c, 也就是事务协调者,它的主要作用呢,就是来向各个子服务呢,去下达全局事务提交或者全局事务回滚的这个命令, 那么它就相当于一个司令官来统筹我们整体的事务状况。那下面呢,我们就从最简单也是最容易理解的 a t 模式入手,了解一下它的使用场景和使用特点。作为 a t 模式是阿里塞塔独有的模式,它通过反向 生成 c 口语句的方式来帮助我们实现这个全局的数据的最终已知性。什么意思呢?来咱们看上面的图, 订单服务来新增订单的时候,是一条 insert 语句,那仓储服务对库存进行减少的时候,是一个 update 语句。按照正常的操作来说,那刚才在我说的两个服务中,是不是底层都要执行一个 insert, 一个 update 来完成数据操作啊? 那在这个过程中,其实作为 sayta 的 a t 模式呢,它呢是要求我们在每一个这个数据库中额外新增一个表,这个表名为叫 and do log, 这是 see 它的强制要求。那么这个 indoor log 表中呢,它要保存的是自动生成的回 滚 circle, 啥意思呢?就是我们利用 set 向数据库中执行 insert in two 语句的时候,在订单库中,它同时在 indle log 表中去执行一个 delete from 订单 vr id 等于一零零一,你可以发现他这里一个新增,那他就对应了一个删除,那 id 号呢,也是相匹配的。而这个自动删除的命令呢,是由 cat 底层的组件自动进行思考解析并反向生成的。 那与之类似,仓储服务对于仓储的这个存量数据呢?他呢进行了数据的减少,原先假设是二百一十,现在出库十个,现在呢变成了二百,那回滚日式中底层就记录了仓储表里边他的原始的值是二百一十,那这个安 logo 什么时候去使用它呢?假设我们前面的这个商城订单创建成功,库存也减少的话,代表整个事物就完成了,所以由 set 的 tc 这个事务协调者发起了全局事务提交以后,那么作为 这里的每一个回滚的 coco 就自动的被删除了。注意,回滚 coco 当遇到全局提交,就会被自动的删除,也就是说不用回滚。但是 如果此时 tc 呢,他发现我们刚才执行过程中,哎,比如说仓库扣减出错了,那我们要进行全局的回滚,那他此时由 tc 呢,会向每一个子服务下达回滚的命令,当收到回滚命令以后,由 cta 自动的去提 提取这个回滚表里边的对应的 co, 完成这个 delete 删除的操作,也就是说把刚才新增的订单记录呢,利用这个 delete 语句反向的来把它清除掉,这就是 at 模式,它的一个实现原理,整体的处理呢? cole 的生成, 回滚的这个数据删除或者还原,它都是由 set 自动完成的。那么作为 a t 模式呢?它的 使用特点呢,也非常的明确,首先呢性能还是不错的,因为它是基于 a p 的设计,保证了用户的可用性。 但是大家发现没有,刚才我在描述的过程中,其实是存在中间数据不一致的情况的。也就是说,在我们订单创建,但是库存还没有完成减少的时候,是不是就是一个不一致的状态? 存在中间状态,如果是在数据比较敏感的场景下,这种中间状态可能会带来一些额外的风险。 那么从使用角度来说,它呢使用非常简单,因为 set 呢,只是靠反向 c 口呢来自动的实现的回滚,我们大多数情况下不用关注底层的执行过程, 那使用要求呢,它这里主要有两点,第一个就是我们的所有的服务和数据库呢,都是要 我们自己开发拥有管辖权的,一般来说这个所有服务都归属到某一家公司,他们自己有数据库的管理权,因为我们要向每一个数据库中去额外增加安度 log 表, 那你总不能说要求其他公司配合你也要在他们自己数据库中加 undo log 吧,那有的时候这种情况很不现实。 第二个呢,就是对数据库的支持上,目前呢支持最好的肯定是 mac circle 或者 maria d b 啊,官方也说对 p circle 也支持,但是我没有测试过,不敢说 啊。那应用场景呢,是针对于高并发的互联网应用,允许出现短时的数据不一致的情况,一般来说,互联网的这种高并发应用,他呢为了保证数据的最终已知性, 会增加一些,比如说对账程序啊,或者补路程序呢,来保证我们数据是最终一致的,这是 at 模式的特点。那么我们再来说说 tcc 模式, t c c 模式呢,是一种反人类的模式,这里的 t c c 是三个英文单词的首字母,尝试确认和取消,那它对应了也就是三个步骤,分别是 try 呢, try 的阶段呢, 是对应的对于资源的锁定, t c c 模式下所有的数据提交和回滚都是要我们自己来实现的,所以呢,作为 t c c 是需要对原始的数据表进行修改, 在原始数据表中,假设只有订单编号,订单金额三个字段,但是呢,在 t c c 这里呢,我们还要额外增加订单状态和预增金额。 同时呢,在这个商品表里边,除了原有的库存以外,还有一个叫冻结库存。那么在我们 tcc 里边,首先第一个踹阶段呢,就是对我们的这个订单呢来进行一个冻结,具体冻结的措施是 新增订单的时候,那我们把这个订单的金额呢,首先写入到预增金额,注意这个字段叫预增金额还没有真正的产生,那 同时呢,当前的订单状态为初始状态,那下面呢,这个冻结库存,那我也是放到了冻结库存这一列,不是在当前库存中来进行减少,这是我们在踹准备阶段要做的事情,他把这些资源呢都先进行了一个预制的处理, 如果所有的数据库都预制成功了,下面呢由 sata 的 t c 呢去下发全局提交,也就是 confirm, 那我们就要对数据呢完成这个业务,具体的完成就是在订单服务中来底层数据表把我们预增金额加到这个真正的订单金额中,状态变成完成。 同时呢库存服务呢,也是按照原先的计算规则,一百减去十,剩余九十完成这个处理。但是如果我们这 这里的这个订单他中间出现了问题要取消的话怎么办?那就是把我们的这个当前的订单的状态变为取消,哎,取消订单吗?同时刚才冻结的不是十个库存吗?现在我们把这个冻结的库存十个给取消掉,那原始的库存不发生变化, 这样相当于整体呢就进行了回滚。那以上呢,你发现没有, t c c 呢?是针对于在数据表上面来做操作, 需要将哪些资源来进行一个冻结,那么它的特点是啥呢?首先性能还是不错的,他不会对资源进行长时间的锁定, 也是基于 a p 模式。哎,你看每一次操作完了以后,它是不是直接提交了呀?那数据的响应速度是相对快的,但是会存在跟 a t 模式一样的不一致的情况,它使用起来真的挺 反人类的。作为 c 塔,刚才我们说到的 t c 呢,就是事物的协调者,他只负责全局的事物的提交和回滚的操作,但回滚和提交他具体怎么做完全 是要靠程序员自己手撸代码来实现的,因为作为 cta, 他是不知道哎在什么阶段,哎,这个状态怎么变更?他这个金额到底是减跟哪个字段来进行交互,是加还是减他是不清楚的,所有的都是靠我们程序员代码来实现的,所以 t c c 呢,现在在工作中其实用的是比较少的,太恶心了。那么 t c c 呢,它有一些使用的要求,首先呢,因为涉及到要需要新增列,所以呢,要求我们自己的服务和数据库都有管辖权。第二个呢,就是他从这个数据库选型上来说 说,并不是特别的严格。支持易购数据库,你可以在数据库使用 masco 数据库,你可以在库存库使用 l reco 或者 pco 或者其他的数据库。因为所有的操作细节都是靠我们程序员自己来实现的吗? 但是呢,这里我还是提点建议,我们选择的这些易购数据库要支持事务,这样呢才可以保证单库操作的时候多表 事物是一致的。这是 t c c 模式的一个应用特点,那么它的应用场景呢,和 a t 基本是一致的,也是高并发的互联网应用,允许短时一致,可以通过对账程序或补路呢来保证最终一致性。这是第二种模式。 那第三种模式呢,是 saga, saga 模式在我们平时开发的时候非常的罕见,但是呢,它也有着自己独立的应用场景。 saga 呢,在这 这个 set 中呢,它是提供尝试物解决方案的,同时呢,它是通过状态机的方式来进行数据的补偿操作。 什么意思呢?来我说一个场景你就明白了。现在呢,我们要进行一个业务处理,那在业务处理过程中涉及到了三方支付,那 比如和微信、支付宝进行对接,要知道支付宝和微信他们是一个额外的组织,是不会受你受到你的管辖,这个关于支付宝的数据库你也无权去访问,所以像前面的 a t 和 t c c a 模式呢?如果在这种长事物和多节点, 尤其设计到第三方的情况下,其实是很难落地实现的,那么萨嘎呢,他就是解决这种问题的。在萨嘎中通过状态机的形式呢,为每一个操 操作步骤呢去拟定了一个正向的处理,比如说第一步是创建订单,第二个是支付,第三个扣减库存。哎,咱们按照这个来理解,作为状态机,那他还有一个逆向的处理,就是比如说 t 一对 c 一的时候,他是代表了这不是创建订单吗? 那对应呢就是删除订单,而 t 二刚才说的是支付是吧?好,那么支付那它对应的反向操作呢,就是退款, 而这个支付和退款往往都涉及到第三方来进行处理,所以是要调用第三方接口来实现的。 a t 三呢,涉及到库存的减少,那么到 c 三呢,就是库存的增加,所以 c 它呢,它是这样的一个基于类似于像事件流或者工作流一样,我们有这么一个流程引擎,帮助我们一步一步的去规 规划每一步,同时呢再给出相应的反向操作。那么沙嘎模式使用起来是比较复杂的,而且性能呢是 确定的,因为往往呢撒卡模式是用在和第三方交互,比如说和支付宝支付啊,或者像其他的这个机构服务进行调用的时候,像这种三方他执行的时间长短,往往就决定了咱们整体的这个操作的时间长短。 那作为 saga 呢,它是基于 a p 模式来实现的,也是存在中间的数据不一致的情况,那使用难易度呢?是 比较复杂的,他提交回滚的这些流程都要我们去增加这个正向和反向的节点。那至于使用的场景呢,就像现在咱们界面上看到了一个像画流程图一样,正向 接点,反向接点怎么处理?那作为萨嘎,他的应用场景是往往出现我们需要和其他第三方交互的过程中,第三方不受咱们管理,但是我们又要去保证最终的一致性,例如现在这种情况,现在呢,我们调用了支付宝的支付接口,相当于付款成功了,是吧? 但是紧接着在后面的处理中,我们自己进行出库的时候,出库失败了,那这时一旦出库失败,整个业务流程要回滚,他呢就会去调用这个支付宝的退款接口,把刚才支付的钱呢给充掉。 这就是我们说到的 saga 的一个应用场景,往往和三方交互的时候,为了保证数据的最终一致性所做出来的。那么他在使用的时候,需要在当前的工程,我们引入状态机机制,也就 是类似于像工作流的这样的一个引擎,他本身是比较重量级的,而且对这个事情的掌握的人不多,那这种情况下他的上手难度还是比较高的。 同时呢,因为在这里我们不涉及到具体的某一个事务俱护的选型,所以他是无法保证这个隔离性的。那这是第三种模式, 最后一种模式呢,是 x a 模式, x a 是最简单粗暴的,在我们数据库中会存在一种叫 x a 的协议来保证,通过两个阶段呢,保证数据的最终一致性。 x a 呢?它呢?说一下场景,我一说你就明白了,也是最好理解的。 我们 t c 新版的这个 sata, 它是支持了 x a 模式,比如说上边儿和下边儿两个都是 mac 或数据库,它呢,都是支持 默认支持 x a 协议的,那就以我们刚才的这个案例为准。这个订单服务和仓储服务,当我们在 x a 第一个阶段,订单服务他完成了 insert 语句的执行,注意此时订单服务并不会提交这个 insert 语句所产生的新纪录, 不提交就意味着这条数据被阻塞死。而仓储服务也是一样,它 update 减库存的时候,也是 第一个阶段先把这个 update 执行了,在我们事务中把它数据先减了,但是它不会提交。对于当前这一条仓储数据来说,它也是会产生一个阻塞的。 假设作为订单服务和仓储服务,他们都这两条四扣都执行成功了,于是由 c 他 t c 呢,在下发一个全局 提交的操作,那每一个具体的咱们服务呢?来执行 comet。 于是在我们最终的数据库中,订单表有了数据,仓储表也进行了库存扣减,这就是我们的 x a 的方案。 如果在刚才过程中仓储执行失败了,那作为 t c 呢,它会下达全区回滚的命令,那在我们订单的这个表里边,直接会执行数据库的 robac 回滚命令,把刚才事务区里边的数据给清除掉,这就是 x a 协议。 xa 协议呢,是依赖于数据库的这种特性来完成的,所以呢,它是强一致性的,我们不会出现中间状态,但是并发量是很低的,因为刚才你发现没有新增的订单会被 hold 住,卡死在那,而我们的仓储 土的某一个商品的这条数据,因为也是进行了库存的扣减,他也是会卡死全局,尤其只有唯一的一个县城呢,可以对他来进行访问,其他的县城都属于阻塞的状态, 这也是我们说到的强已知性他的一个问题所在。那么使用的难易程度上,确实这种情况非常简单,因为 这是数据库自带的事情。我们部署好了 set 的 t c 以后,它呢,不用做额外的太多的调整呢,就可以让我们在不修改表的情况下完成这个数据的一致性,那它的使用要求就是所有 支持 x a 方案的关系数据户都可以使用。同时呢,应用场景呢,在金融行业,因为金融行业涉及到钱,往往不允许 出现中间状态,对吧?那所以针对金融行业,在并发量不大的这种业务上,采用 x a 保证数据的全局的强一致是很有必要的。好,那讲到这里呢,我为你介绍了在这个分布式事务,以 seta 为代表的 a t, t, c, c, saga 以及 x a 模式,它们分别的使用的原理和它们对应的应用场景,希望能对你有帮助。
粉丝5901获赞1.9万

我们说到分布式事务呢,比较常见的就是这个插爱模式啊,基于数据库的事务,还有呢是 tcc 啊,基于补偿模式。那么还有一种呢啊,是这个 at 模式啊,用的也很多是阿里的这个 ct 推出的, 那么这两天呢,正好有一个分布式事务要写,我就用了一下这个,呃, it 模式还挺好用的,真的,就像这个 c 台文单上写的那样,没有做任何的代码侵入,只要引入一些啊简单的依赖,添加一个格罗波全赛克声哦,这个柱姐 对吧,那么所以他管理器呢,就帮你把这个事务给做了,确实呢,还挺方便的。那么说到这个 at 模式的原理啊,啊,也很好理解啊,同样也是两个阶段,一阶段呢就是提交数据啊,比如说你下单 哦,涉及到了 qq 存啊,又涉及到了出现订单,那这个时候呢,我们通过格罗波这四个神喽,这个柱姐呢啊,他就做了这个 aop 啊,前面拦截对吧,你的所有的搜口啊,每一个分支对数据库的操作都会记录在这个安都 logo 里面, 但是这个 log 呀,啊,不是数据股级别的,这个回滚日制,它是 c 塔自己解析的这个搜索语句啊,形成的日制,详细的记录了每一个步骤啊,这个是一阶段, 那么二阶段呢,就是判断每一个分支是不是都成功了啊,如果都成功了呢,那么之前写的 anglelog 回滚日制呢,就直接删除啊,全局所示范掉,那么其他的操作啊,可以进来,那么如果有一个分支失败啊,那么就直 行对应分支的安多罗尔的回款日制,原来扣减的库存,你得恢复到原来的数值,创建的订单呢也得取消掉,这个就是 at 模式的一个原理,核心呢就在于啊,代码没有侵入啊,只通过注解的方式就能够分析出数据库做了什么事情 啊,并且呢形成了一个啊叫做安都 logo 的日制啊,这些都是在 c 的层面来做的 啊,虽然很简单啊,也很方便啊,用的很爽,但是呢还是啊有一些问题的,比如说一个师傅在运作啊,在下单,在扣库存啊,在创建订单,这个时候呢,如果有另一个七线城过来, 这个时候呢是不让他读的啊,容易发生单独,怎么解决呢? ct 呢,就直接左侧了,所以呢确定还是很明显的性能不是那么的好 啊,在当前事务没结束的情况下面,全局所会把其他的县城主宰在门外,所以呢他其实是牺牲了一定的这个并发能力 啊,如果在一些这个性能要求比较高的场景,还是推荐大家用一些这个其他的补偿型的失误模式,比如说像这个 ttc, 像萨嘎,对吧, 虽然侵入代码,但是在并发性能上面要好很多。好了,本期的视频呢就这些了,如果您对本期的内容呢,有任何疑问,欢迎大家在评论区给我留言,谢谢大家!

tcc 其实有点类似二 pc 的准备阶段和提交阶段,但 tcc 是位于用户代码层面,而不是在基础设施层面,这为他的实现带来了较高的灵活性,可以根据需要设计资源锁定的力度。 tcc 在业务执行时只操作预留资源, 几乎不会涉及所和资源的征用,具有很高的性能潜力。但是 t c c 并非纯粹只有好处,它也带来了更高的开发成本和业务侵入性, 意味着有更高的开发成本和更换事物实现方案的替换成本。所以通常我们并不会完全靠裸编码来实现 tcc, 而是基于某些分布式事务中间见,比如阿里开源的 set 去完成,尽量减轻一些编码工作量。

在 r b c 两阶段提交协议中,事务管理器分两阶段协调资源管理。资源管理器对外提供三个操作,分别是一阶段的准备操作和二阶段的提交操作和回滚操作。 t c c action 是 t c c 参与者,实力参与者需要实现三个方法, 第一个参数必须是 beats suck 胜 contact 方法,返回类型固定,对外发布成微服务公式。物管理器调用。一、 prepare 资源的检查和预留,立,扣减账户的余额,并增加相同的冻结余额。二、塔 met 使用预留的资源完成真正的业务操作。立, 减少冻结余额,扣减资金,业务完成。三、谈色释放预留资源利冻结余额加回账户的余额。其中 毕竟 suction contact 封装了本次事务的上下文环境 是嫩和贝拉比斯的斯啊春康 x 他们注解的参数等。参与方业务有几个需要注意的地方,一、控制厌恶密等性, 需要支持同一笔事务的重复提交和重复回滚。二、房悬挂及二阶段的回滚比一阶段的突然间执行。三、放宽一致性,协议最终一致,所以是独一修改。

哈喽,各位小伙伴们大家好,我是一锅炖不下的北冥。那么这一讲我们所聊的面试题呢,是去描述我们撒岗模式的基本过程,以及他在我们分布式事物当中的一个应用啊。那么这道题的难度呢,我给他定在了一个三星对应的岗位呢,是我们中高级的工程师啊。 我们的撒感模式呢,实际上也是用于解决分布式事物当中的一种方式啊。他的一个主要思想呢,是把我们复杂的一个事物拆分成多个小事物。跟大家举个例子。 我们可以看到下面这张图啊,假设我们会有很多的服务,比如说我们的订单,我们的支付,我们的仓储,我们的物流,对吧,等等等等。假设我会有一个下单的场景,那么我们通过我们的主程序开始。 这个时候呢,我们的这个主程序呢,他会去向我们的事务管理器呢,去提交一个 sata 事务。然后我们的主程序呢,他会把 他自己本地的事务呢去提交掉,然后再通过我们事件的方式去通知我们下面的服务。比如说我们的服务微服一,微服二,微服三,直到全部成功。 那么这个时候呢,我们一整个分布式事物的流程呢就走完了。如果说在中间过程当中,某一个服务发生了异常,假设我们的微服二出现了错误,对吧?那么从我们微服二开始去进行一个反向回滚呢。这个呢,就是我们三个模式的一个流程啊。事实上我们去实现我们三个模式呢,他会有很多种的方式。 那么其中最流行的两种方式呢?是我们的基于事件的方式和基于命令的方式。那接下来呢,我们来具体的去看一下。首先呢,我们来去基于事件的方式去跟大家去进行讲解。 我们还是以我们电商场景举例啊,我们会有订单支付,那么库存还有物流对吧?好,四个服务基于事件的方式。它的一个原理是这样, 假设我们的订单服务他在执行完本地事务之后,他会去产生一个新的事件,那么我们其他的服务呢,他就会去监听这个事件啊。我们可以看到下面标注的第一个位置,我们可以看到这个是我们订单创建的事件对不对?而我们的支付服务呢,他就去监听我们订单创建的这个事件。如果说我们这个订单服务 他发生了一个订单创建的一个事件,那么我们的这个支付服务呢,他就能够接收到。然后我们支付服务呢,他就会去执行他本地的事务,然后我们的支付服务他再去产生一个新的事务。那么我们的库存服务呢,他就去监听我们支付服务的这个事务。那么以此类推啊, 往下去循环走。那么直到最后呢,是我们这个物流服务对吧?那么他一整个流程呢,就是通过这种基于事件的方式来去实现的我们的分布式事务啊。那么可能会有同学会问,虽然通过这种 方式呢,却是能够解决分布式事物的一个问题,但是如果说其中一个服务他出现了错误,那么我们应该怎么去处理呢?我们来看到下面一张图啊,如果说我们在异常的情况下,我们要去回滚我们整个分布式事物, 那么我们要为相关的服务呢,去提供一个补偿操作的一个接口啊。假如说我们订单创建完对吧?那么到了我们的支付服务,那么支付也成功了。但是我们再去进行库存服务的时候,我们会发现本地事务呢,无法提交,因为我们的库存不足,那么他没有完成备货对吧? 那么应该怎么去回滚呢?我们的订单服务呢,他会去发出一个我们库存不足的一个事件啊,而我们的订单服务和我们的支付服务呢,他们去监听了我们这个事件。如果说出现了库存不足的服务,那么我们的订单呢,他就会把订单状态设置为失败状态。而我们的支付呢,他就 会把已经扣款的余额,然后去进行一个退款。这个呢,就是我们基于事件方式的一个回滚处理啊。那最后我们再去总结一下我们去基于事件的方式,他的一个优缺点,他的优点实际上也很明显对吧,非常的简单。然后呢,也很容易理解。 那么我们各个参与方之间,他本身是没有直接联系的对吧?完全只有的。然后呢,我们也不需要我们的事件协调器对不对?那么这种方式呢,比较适合我们分布式事物里面只有比较少的步骤的一些情形啊。 如果说我们的业务参与方案非常多,我们有几十个微服都要参与,那么这个时候我们去做这种监听的方式呢, 是非常非常复杂的,而且呢更不利于维护啊,甚至有可能会造成我们的环形监听,而我们基于命令的方式呢,他就能够去克服我们在这里所说到的一些缺点啊。那么接下来呢,带大家去看一下我们基于命令的 方式呢,我们会定一个新服务,那么这个服务呢,我们可以把它叫做一个协调器啊,他呢就像一个指挥者,他会告诉我们各个的业务参与方,包括支付啊,库存,物流对吧?他去告诉他们你们应该在什么时候去做什么事情。 我们还是以一个电商场景去进一个举例啊。我们的订单服务呢,他创建了一笔新订单对吧?那么这个时候呢,他把他本身的一个订单状态设置为代处理。 然后呢,他去通知我们的协调中心,他去开启一个创建订单的一个事务。那么这个时候呢,我们的协调中心他就会去发送好几条命令啊,执行我们的支付命令,那么执行我们的这个备货命令,那么在执行我们这个配送命令啊, 那么我们的支付、库存和物流呢?他们去监听我们消息对内里面的这些消息,然后我们再通过我们消息回复的这根管道去进行一个回复。 如果说我们中间出现了异常,那么回滚操作是怎么实现的呢?我们来去看一下,假设我们还是刚刚的那个问题,我们的这个协调中心呢?嗯,他给我们三个服务去发送消息对吧?那么前两个呢,都已经成功了,那么事务呢?也执行了。 那么我们会发送最后一条消息的时候,哎,他发生失败了,我们的库存呢?不足,这个时候呢,我们就会去触发一个回滚操作,那么我们的这个库存服务呢,他就会去通知我们这个协调中心,我这边的事务呢,并没有成功的提交。那么这个时候我们的协调中心呢,他就会去给我们的 支付服务和我们的这个订单服务,该退款的我们去退款,那么该改订单状态呢,我们去改订单状态。那么这个呢,就是我们基于命令的方式啊。那么基于命令的方式呢,它的一个好处,相对比我们前面所介绍的那种, 他避免了我们业务方之间的环形依赖对吧?他们依赖的呢,都是我们的协调中心,而服务跟服务之间呢,都是一个隔离的存在啊。而且我们从可维护性上来看, 这种方式呢,实际上是非常非常的清晰对吧?而且呢,也减少了我们业务参与方的一个复杂度嘛。我们也不需要去监听我们各个服务之间不同的一些消息类型, 我们只需要去响应 mini, 然后回复消息就 ok 了。包括我测试和回滚起来呢,也非常的简单。那么关于沙岗模式的一个讲解呢,我们就到这里就结束了,如果大家有需要我们这里面的这份笔记的话,那么可以评论区扣一领取啊。那么相关的细节呢,我也是写的非常清楚,在我们笔记里面了。

对于分布式事务的典型的处理场景,一般来讲就就三种场景,第一种场景就是用传统的基于 x a 的两阶段提交协议来完成分布式事务的一个处理。两阶段提交简单来讲就是什么呢? 对任何一个提交动作,把它分为 prepare 的欲提交和正式提交。两个动作执行的时候首先都要先去完成欲提交, 欲提交没有问题,大家都说欲提交成功了,然后我再来去做正式的 commit 操作,如果 commit 出现了错误,然后我再对我欲提交的内容进行回滚, 这个就是一个典型的 x a 的两阶段提交协议。如果你要用两阶段提交,那么你当前的数据库应用中间键或者是你的组件必须要支持 x 费的两阶段提交协议,这个是使用 x a 分布式事务的一个基础条件。对于两阶段提交,它对于有一些异常情况的处理,它并不一定说百分之百能处理。典型的例子就是什么呢? 比如我刚好已经预提交成功,第一个节点也提交成功,整个事务协调器资源荡机,这个时候他是没办法去再去做回滚的操作的。或者说两阶段提交涉及到数据库的时候,有时候也会造成数据库的一些死锁, 这些情况其实很难去避免,也正是这个原因,你可以看得到,当在当前的微服务下面,其实 x a 两阶段提交用的并不是特别普遍,这个是我说的第一种方式,第二种方式就是急于弱一致性的失误补偿, 事务补偿。简单来讲就是什么呢?当我再去实现分布事务的时候,对于任何一个我通过 web service 接口来实现的操作,我必须要去写一个逆向的接口,通过调这个逆向的接口来实现正向数据的一个回滚。以任何一个 a 的服务实现, 你必须要有一个负 a 的服务和他对应,同时两个服务还必须具备我们常说的密等性,这个是我们去做事务补偿的基础。对事务补偿,我们举一个简单的例子,比如一个采购订单的提交, 在我去进行采购提单提交的时候,我必须要在对实际的库存进行扣减,如果是调到两个 web service, 那就典型地形成了分布式事务。当我去用事务补偿方式来实现的时候, 首先我去掉采购订单保存的 service 操作,然后接着我再去掉我的这个库存扣减操作。如果采购订单保存操作出现了错误, 那么我就在调用库存回退的或者是库存增加的这个逆操作,把我原来扣减的库存再回退回去,这个就是一个典型的事务补偿。最后一个方式就是基于拜斯理论的事务最终一致性,这个也是我们当前很常用的一种分布式处理方式, 他并不是强调在某一个时点你马上就要达到事务的一致性,而是更加强调了你只要最终达到事务的一致性就可以了。所以机遇事务一致性、最终事务一致性常用的方法,他一定会和消息中间键去配合来完成。什么意思呢?就是 是第一步分布制事物的第一步操作你再去做,处理完了以后,你会把你的第二步操作不是直接去调 web service, 而是把你的第二步操作请求写入到消息中心镜中, 然后通过消息中间建议部去处理这个请求,去掉下一步的一个操作。第一个首先通过消息中间键可以实现结偶。第二个消息中间键它本身就有重试的机制, 如果调调用下一步操作出现错误,他还可以发起重试。我们举一个简单的例子,比如我们还是用采购订单来举例, 就是我们在提交采购订单的同时,我们希望去启动工作流引擎,把这个工作流审批流启动起来。这个时候一样的会涉及到两个操作,一个是采购订单的 保存,一个是调用 star workflow 的 web service 启动工作流,那这个时候就可以用最重一致性的方式来解决。首先第一步就是调采购订单保存的 service, 把订单保存成功。第二步把启动工作流这么一个任务,通过调用消息中间键的方式, 把这个请求写到类似于 active mpu 的消息中间键中,订单保存的这个请求和写入启动流程请求到消息中间键这两个东西我是可以用 xa 分布式事务两阶段提交来把它控制到一个事务里面的。当写入到了消息中间键里面的请求, 我又可以通过消息中间键的一步机制去调用你工作流提供的 start to workflow 的接口来启动流程。如果出现错误,那我就可以重试。那如果三次四次重试 是他仍然出现错误,怎么处理?在这种情况下,不是对上一部订单保存操作去做回滚,而是我们需要去人工干预这个流程,对于多次启动流程失败的,我们就去人工干预去处理, 因为整个操作其实跟订单提交已经没任何关系,你流程启动失败是你流程引擎的问题,你必须要人工干预去把它处理掉,这个就是我们常说的分布式事务的常见的三种方式,今天的分享就到这个地方,谢谢大家。

呃,说到分布式架构,说到微服务啊,只要涉及到交易啊,涉及到多个库,那么就一定绕不开分布式事务。 在很多的场景下面呢,我们需要保证说出了一致情,那么就需要保证要么全部成功,要么全部失败。不可能说啊,我支付成功了啊,我的优惠券没抵扣对吧,我的积分没加上这种呢啊,导致了后果啊,大家可以自行脑补。 总而言之呢,事物在整个的系统中发挥着比较重要的作用,也是分布式相关面试中着重考察的对象。 分木式的事务的主流的实践方式呢,其实就两种,一种呢是基于拆协议啊,一种呢是基于 ttc 拆协议的原理呢,其实就是两阶段提交,而且呢依赖数据库本身的事务。在 事务开始之后啊,事务管理器呢,先询问各个数据库准备好了吗对吧啊,如果每个数据库都回复 ok 啊,那么就正式提交事务在各个数据库上面来执行操作。如果有任何一个数据库回答不 ok, 那么呢就回滚事务。 举个例子啊,你要购买商品啊,你有抵扣券,你有积分啊,你就会先利用说句故,事物呢啊冻结你的积分啊,你的抵扣券,然后购买成功之后呢啊,各个事务正常再提交啊,然后把冻结的积分啊优惠券真正给扣掉。 这个方案呢相对来说稍微简单一些啊,但是呢比较依赖啊,数据库的事务效率呢也不高。有同学也可能会说啊,差异协议是不是只能作用于这个单服务内部的这个多数据库场景啊,其实跨服务 的多资源场景呢也是可以的,只不过呢,同样需要额外的这个事务传递机制。我们再说说 ttcttz, 其实跟插 a 协议比较大的区别呢,就是不需要依赖数据户的事务采用呢,也是这个不长机制来处理数据的一致性问题。 有三个阶段啊,一个是踹,一个是 cofoam, 一个是 cocyo 踹这个阶段呢,对于业务系统呢,他是做了检测和资源预留的作用,比如在交易系统中啊,要抵扣的积分 啊,就先啊通过业务代码先冻结,然后再坑父母阶段呢啊,主要就是对这个业务系统做确认提交。这个阶段呢,就真正开始扣减你的积分了啊,该扣积分扣积分,该扣优惠券扣优惠券,该支付的支付。那么如果遇到 到错误呢,就需要直接回滚,之前冻结的积分呢,就直接释放了。 tcc 呢啊,代码的侵入性比较强啊,每个阶段呢,都需要业务代码来实现,因为不依赖收据过的事物啊,回滚呢,是需要通过补偿机制来实现的, 比如说支付失败啊,就需要返还啊之前扣除的积分啊,就比如说我们可以通过发送这个消息啊,到这个积分中心,让积分中心啊通过业务代码来处理积分的房外。 tdt 呢,在分布式架构下啊,用的更多一些啊,不像 xi 那样占用太长时间的这个数据库资源,所以在性能上面呢,要稍微优秀一些。当然除了 xiattp, 还有利用这个消息队列做事务啊,先发这个办消息, 然后执行主业务啊,成功之后呢,办消息就转成正常消息来进行消费啊,我们也在前面几期讲这个消息对待的时候呢,也专题讲过啊, 感兴趣的同学呢可以去翻一下。好了,本期的视频呢就是先来,如果您对本期的内容呢有任何疑问,欢迎大家在评论区给我留言,谢谢大家!

好,那我们再来看什么是分布式事物?有哪些实现方案? 好,我们来看。首先什么是分布式事物啊?我们在分布式系统中间,我们一次业务处理很有可能会涉及到多个应用,对吧?比如说我们的 客户在网页上面去下一个单,下一个订单买一个东西,那么这个中间在下单的时候可能就会涉及到多个系统,比如说就是遇到订单系统,你要去创业订单库存系统你要去剪库存, 而对于一次下单,我们订单创建和减库存应该是要同时成功或同时失败的,但是呢,在分布系统中,我们如果说不做任何的处理,就很有可能会出现订单先创业成功呢,但是呢库存减失败了,对吧?那么这样解决这种问题就需要 要用到我们的分布式事物,最常用的解决方案呢?有什么呢?第一个本地消息表。首先什么是本地消息表?就是在我们的数据库里面,我们会有一个表啊,那么这个表去存什么的?大家看 我们在创建订单的时候,可以将剪库存的这个信息啊,比如说我现在是哪个订单,我要剪多少,对,我要剪哪个货品等等,对吧?那么我在创建的时候,我就把剪库存所对的这些信息一起的加到我们的本地事物,因为我创建的呢,我本身就是一个事物,对吧? 我就把我检库存这个存放存到我刚才说的那个消息表里面去,像这条收口,我也把它写在我当前这个创新订单的这个书里面,那么到时候就一起提交到数据库去,存到我的本地消息表里面,然后我就去存完之后我就去调检库存,如果说我检库存成功了,那我 就把把刚刚我在存在本地消息表里面那个消息的状态改变成功,如果说我仅库存失败了,那么我到时候会有一个后台定时任务,从本地消息表中间去取出呃,那些呃检库存没有成功的消息,我就从事去调用库存系统,所以这他保证就是 我在存订单的时候,我在创建订单的时候,我会啊把这个剪库存的消息存到数据库里面去啊,这一步和订单是否创业成功,这个是一样的,要么都成功,要么都失败,所以说订单如果是创业成功,那么我这个剪库存的消息我肯定就存到表里面去了,因为你是在同一个失误里面,本地失误里面的,对吧? 回头啊,我如果说剪库存失败了,那么我还有从我就从本店消息员里面把这个消息剪库存的消息,又把它给拿出来,又重新的去剪库存,对吧? 啊,所以说是这个,当然你因为你存了之后,其实你想做很多其他的处理也是比较方便的啊,这是第一种方式,本地消息表,那么还有我们的消息对列的方式,那么像我们目前啊,如果给在门口中间就有支持事故消息,他的一个工作原理是这样子,首先, 呃,生产者就是订单系统,我先,哦,我在下一个订单的之前啊,我会先发一个哈弗消息给到我们的波尔可啊,这是看胸的,这生产者,好,那么这是我们的波尔卡啊,那么这边呢,就是我们的 prouse 生产者,呃,销,呃,就是 啊,这是消费者啊,反了对不对?这边应该是 p 啊,这边应该是 c 啊,画一下啊,这是我们的生产者,这是我们的消费者。好,那么我们现在的订单系统,他一开始呢,就会发送一个哈弗消息给我们的布洛克啊,发现一个半消息,那么这个半消息 对于我们的消费者而言,他是不可见的,所以他不能消息,哪怕他不能消费,那么我们啊,紧接着我发我生产者这边,我订单系统,我发送完这条消息之后,我就去创业订单啊,创业订单如果成功了,那我再向我们的 book 去发送一个卡密特, 那么就让我们的汉服消息呢,变成一个正常的消息,那么这种情况,下面我们来看书本,他就可以消费这条消息了, 前提是我订单创业成功了,创业成功,我就把这个消息的状态给改了,改了之后你消费者你就可以来消费了,但是如果说消费者你消费这条消息的时候,监控成消息失败了,对,那么我们这个消息肯定会进入到,比如说你可以进入到我们的 死心堆那里面去,然后不停的去从事。啊,本来你消费一条消息如果说失败了,你本来就有一些从事的策略,对吧?啊,所以你可以不停去从事,如果从事策略还是失败了,那么就从这点消 会进入到死性对内,那么就看你接下来该如何去处理好,这是正常情况,但如果说我 作为我的订单系统,我发送完这个汉服消息之后,我去专业订单,如果说创建订单失败了,那么我就把我就去发送一个肉贝壳命令,让刚刚发送出来的这个汉服消息呢,又把它删掉啊, 所以这个消息相当于没有发过,因为我订单创业失败了吗?所以我也不可能会去解库存啊,就是这种,当然还有一种就是如果说啊,我订单系统这边我发送完汉服下去之后, 我接下来就没有任何的动作了,我看秘神人也没有发,我若半个也没有发,那么这个时候像洛克那门沟里面,他会就去检查啊,他自己现在里面存的这个,呃,哈弗消息怎么过了一段时间还没有及时到任何操作命令,他会主动的来调我们订单系统的某一个接口,这个接口你可以 去指定啊,啊,那主动来调,哎,看我,我们刚刚你这条哈府消息,你对应的订单到底是不是创业成功了? 有可能你订单系统把这个订单创建成功了,但是呢,你没有来得及去给我发康密的命令,对,所以说我布罗克我就主动的来回头来调一下,你也看一下这个订单,你是不是到底创建成功了啊?如果说创建成功了呢?哎,那我就把这个哈弗消息又把它改成康密特, 对不对?改成正常的消息,回头你裤子系统这边你再来消费啊,这样子呢,就会比较合适啊,所以这是我们的消息队列的方式啊,大家回头自己再去好好的总 总结一下,好吧,当然还有像我们的西塔框架,这是阿里开元的一个分布式事物框架,现在呢,应该也是御用的啊,越来越多了,他是属于我们 suprenk 的阿里巴巴呃,这一套里面的一个了已经。所以说像西塔这个框架,他支持 at 模式、 tcc 模式等等啊,像,但是这两种我这里就不去多讲啊,大家回头可以自己再去多了解一下。但他们的底层其实都是基于两阶段提交来实现的, 我们的分布式事物我们如何说是基于理论的?呃,话怎么来实现分布式事物的话,无非就是两阶段提交和三阶段提交啊。但是我们目前啊,基本上还是,呃实现了两阶段提交,因为三阶段提交我发现好像还没有什么系统啊真正把三阶段提交把它给实现了的。 好吧,所以说,呃,具体的大家回头可以再去好好的学习一下我们的 c 塔这个框架,好吧。

很多同学呢,一提到 tct 啊,就说是三阶段提交啊, try, conform, cost, you 啊,很自然的就说是三阶段提交,对吧?那么这么理解呢,其实是错的啊,虽然是三个单词,但是啊,实际上 tct 呢,还是二阶段, 这个大家不要回答错了,基本上你要是回答三阶段啊,面试官呢,就不想再往下问了。但是这个二阶段呢,又不是我们在分布式事务里面常说的这个二阶段啊,通常呢,这个二阶段指的是啊,基于这个差异模式,要依赖数据库事务的啊,这么一种模式, 那比如说像订单中心的数据库啊,你就要负责好这个串接订单的相关事务,库存中心呢,你要负责好库库存相关的事务啊,然后通过售后管理器啊,比如说 cta 啊,对吧,来统一管理全程事务,我们先尝试发起啊,如果客户分成 ok, 那么统一提交啊,这是传统的二阶段,我们在前几期呢也讲过啊,这个二阶段啊,感兴趣的同学呢,可以去翻一翻。那么 t t c 这个二阶段呢,它其实是传统二阶段的改进版本,同样也是先尝试发起事物,如果都正常,那么 kimit 啊,如果失败,那就直接 conceal, 对吧?乍一看呢,好像跟传统二阶段也没什么区别,但是却有一个很大的改进,就是抛弃了数据库的事物,所有的事物处理呢,包括尝试处理啊啊,提交回滚,对吧,全都要上升到业务层来处理 啊,什么意思?就是你得自己手写逻辑代码啊,在我们的业务存里面,比如说下单客户存啊,你得在准备阶段就是 try 啊啊,你得真实的创建订单啊,只是订单的状态是预创建的,这样的一个状态没有转正, 对吧?库存的扣减呢,也是在预扣处的状态啊,比如说我们引进了这个库存的预扣表,我们先把这个库存呢先冻结起来 啊,等到各个分支都准备完成了,这个时候告诉收管理器啊,可以提交了,然后呢才是真正的把我们中间记录删掉啊,真正的在库存主表里面把库存扣掉,那么订单状态呢?转为正常状态啊,这个事物呢,就算完成了, 那么如果遇到分成失败的情况怎么办呢啊,这里要注意啊,要分阶段来考虑啊,如果是在踹阶段失败呢啊,比如说库存不足了,那么直接 robot 对吧?这个毫无疑问,但是如果在 commit 阶段失败了怎么办? 这个时候呢,就没法回国了啊,只能硬着头皮不停的重试,直到成功为止。当然这个重试动作也是受管理起来做的啊,如果你代码里面没有 有什么 bug 啊,有充足的测试,而且揣阶段呢,基本上都尝试了一下。那么其实一般的什么 ctrl 吗? ctrl 都是可以成功的。可能呢,有同学会有这样的一个观点,说 tct 效率低啊,这个观点呢,怎么理解呢?其实从啊代码的时间角度来说呢,确实很低, 因为通篇全是业务代码的侵入除外啊,可否我们可能要无一例外?业务代码相当复杂啊,尤其是维护了几个版本之后啊,如果换上来维护就会特别痛苦。 这个相对于叉 a 啊 a t 模式啊,在代码层面就没那么友好,但是呢,我们从接口的效率来讲呢啊,因为没有全局的事务锁定啊,没有依赖数据库事务 啊,线上无阻塞,相对来说呢,接口访问的效率呢,是提高了的。好了,本期的视频呢,就这些了,如果您对本期的内容呢有任何疑问,欢迎大家在评论区给我留言,谢谢大家!