关于java:面试官聊聊分布式事务再说说解决方案

51次阅读

共计 7124 个字符,预计需要花费 18 分钟才能阅读完成。

前言

开始咱们明天的话题,说说分布式事务,或者说是我眼中的分布式事务,因为每个人可能对其的了解都不一样。

分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会波及到的一个货色,特地是在微服务架构中,简直能够说是无奈防止,本文就分布式事务来简略聊一下。

数据库事务

在说分布式事务之前,咱们先从数据库事务说起。数据库事务可能大家都很相熟,在开发过程中也会常常应用到。然而即使如此,可能对于一些细节问题,很多人依然不分明。比方很多人都晓得数据库事务的几个个性:原子性 (Atomicity)、一致性(Consistency)、隔离性或独立性(Isolation) 和持久性(Durabilily),简称就是 ACID。然而再往下比方问到隔离性指的是什么的时候可能就不晓得了,或者是晓得隔离性是什么然而再问到数据库实现隔离的都有哪些级别,或者是每个级别他们有什么区别的时候可能就不晓得了。

本文并不打算介绍这些数据库事务的这些货色,有趣味能够搜寻一下相干材料。不过有一个知识点咱们须要理解,就是如果数据库在提交事务的时候忽然断电,那么它是怎么样复原的呢?为什么要提到这个知识点呢?因为分布式系统的外围就是解决各种异常情况,这也是分布式系统简单的中央,因为分布式的网络环境很简单,这种“断电”故障要比单机多很多,所以咱们在做分布式系统的时候,最先思考的就是这种状况。这些异样可能有 机器宕机、网络异样、音讯失落、音讯乱序、数据谬误、不牢靠的 TCP、存储数据失落、其余异样等等 …

咱们接着说本地事务数据库断电的这种状况,它是怎么保证数据一致性的呢?咱们应用 SQL Server 来举例,咱们晓得咱们在应用 SQL Server 数据库是由两个文件组成的,一个数据库文件和一个日志文件,通常状况下,日志文件都要比数据库文件大很多。数据库进行任何写入操作的时候都是要先写日志的,同样的情理,咱们在执行事务的时候数据库首先会记录下这个事务的 redo 操作日志,而后才开始真正操作数据库,在操作之前首先会把日志文件写入磁盘,那么当忽然断电的时候,即便操作没有实现,在重新启动数据库时候,数据库会依据以后数据的状况进行 undo 回滚或者是 redo 前滚,这样就保障了数据的强一致性。

接着,咱们就说一下分布式事务。另外,分布式事务系列面试题和答案全副整顿好了,微信搜寻​Java 技术栈,在后盾发送:面试,​能够在线浏览。

分布式实践

当咱们的单个数据库的性能产生瓶颈的时候,咱们可能会对数据库进行分区,这里所说的分区指的是物理分区,分区之后可能不同的库就处于不同的服务器上了,这个时候单个数据库的 ACID 曾经不能适应这种状况了,而在这种 ACID 的集群环境下,再想保障集群的 ACID 简直是很难达到,或者即便能达到那么效率和性能会大幅降落,最为要害的是再很难扩大新的分区了,这个时候如果再谋求集群的 ACID 会导致咱们的零碎变得很差,这时咱们就须要引入一个新的实践准则来适应这种集群的状况,就是 CAP 准则或者叫 CAP 定理,那么 CAP 定理指的是什么呢?

CAP 定理

CAP 定理是由加州大学伯克利分校 Eric Brewer 传授提出来的,他指出 WEB 服务无奈同时满足一下 3 个属性:

  • 一致性(Consistency):客户端晓得一系列的操作都会同时产生(失效)
  • 可用性(Availability):每个操作都必须以可预期的响应完结
  • 分区容错性(Partition tolerance):即便呈现单个组件无奈可用, 操作仍然能够实现

具体地讲在分布式系统中,在任何数据库设计中,一个 Web 利用至少只能同时反对下面的两个属性。显然,任何横向扩大策略都要依赖于数据分区。因而,设计人员必须在一致性与可用性之间做出抉择。

这个定理在迄今为止的分布式系统中都是实用的! 为什么这么说呢?

这个时候有同学可能会把数据库的 2PC(两阶段提交)搬出来谈话了。OK,咱们就来看一下数据库的两阶段提交。

对数据库分布式事务有理解的同学肯定晓得数据库反对的 2PC,又叫做 XA Transactions。

MySQL 从 5.5 版本开始反对,SQL Server 2005 开始反对,Oracle 7 开始反对。

其中,XA 是一个两阶段提交协定,该协定分为以下两个阶段:

  • 第一阶段:事务协调器要求每个波及到事务的数据库预提交 (precommit) 此操作,并反映是否能够提交.
  • 第二阶段:事务协调器要求每个数据库提交数据。

其中,如果有任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在此事务中的那局部信息。这样做的缺点是什么呢? 咋看之下咱们能够在数据库分区之间取得一致性。

如果 CAP 定理是对的,那么它肯定会影响到可用性。

如果说零碎的可用性代表的是执行某项操作相干所有组件的可用性的和。那么在两阶段提交的过程中,可用性就代表了波及到的每一个数据库中可用性的和。咱们假如两阶段提交的过程中每一个数据库都具备 99.9% 的可用性,那么如果两阶段提交波及到两个数据库,这个后果就是 99.8%。依据零碎可用性计算公式,假如每个月 43200 分钟,99.9% 的可用性就是 43157 分钟, 99.8% 的可用性就是 43114 分钟,相当于每个月的宕机工夫减少了 43 分钟。

以上,能够验证进去,CAP 定理从实践上来讲是正确的,CAP 咱们先看到这里,等会再接着说。

BASE 实践

在分布式系统中,咱们往往谋求的是可用性,它的重要程序比一致性要高,那么如何实现高可用性呢?前人曾经给咱们提出来了另外一个实践,就是 BASE 实践,它是用来对 CAP 定理进行进一步裁减的。BASE 实践指的是:

  • Basically Available(根本可用)
  • Soft state(软状态)
  • Eventually consistent(最终一致性)

BASE 实践是对 CAP 中的一致性和可用性进行一个衡量的后果,实践的核心思想就是:咱们无奈做到强统一,但每个利用都能够依据本身的业务特点,采纳适当的形式来使零碎达到最终一致性(Eventual consistency)。

有了以上实践之后,咱们来看一下分布式事务的问题。

分布式事务

在分布式系统中,要实现分布式事务,无外乎那几种解决方案。

一、两阶段提交(2PC)

和上一节中提到的数据库 XA 事务一样,两阶段提交就是应用 XA 协定的原理,咱们能够从上面这个图的流程来很容易的看出两头的一些比方 commit 和 abort 的细节。

两阶段提交这种解决方案属于就义了一部分可用性来换取的一致性。在实现方面,在 .NET 中,能够借助 TransactionScop 提供的 API 来编程实现分布式系统中的两阶段提交,比方 WCF 中就有实现这部分性能。不过在多服务器之间,须要依赖于 DTC 来实现事务一致性,Windows 下微软搞的有 MSDTC 服务,Linux 下就比拟喜剧了。

另外说一句,TransactionScop 默认不能用于异步办法之间事务统一,因为事务上下文是存储于以后线程中的,所以如果是在异步办法,须要显式的传递事务上下文。

长处: 尽量保障了数据的强统一,适宜对数据强统一要求很高的要害畛域。(其实也不能 100% 保障强统一)

毛病: 实现简单,就义了可用性,对性能影响较大,不适宜高并发高性能场景,如果分布式系统跨接口调用,目前 .NET 界还没有实现计划。

二、弥补事务(TCC)

TCC 其实就是采纳的弥补机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和弥补(撤销)操作。它分为三个阶段:

  • Try 阶段次要是对业务零碎做检测及资源预留
  • Confirm 阶段次要是对业务零碎做确认提交,Try 阶段执行胜利并开始执行 Confirm 阶段时,默认 Confirm 阶段是不会出错的。即:只有 Try 胜利,Confirm 肯定胜利。
  • Cancel 阶段次要是在业务执行谬误,须要回滚的状态下执行的业务勾销,预留资源开释。

举个例子,假入 Bob 要向 Smith 转账,思路大略是:
咱们有一个本地办法,外面顺次调用
1、首先在 Try 阶段,要先调用近程接口把 Smith 和 Bob 的钱给解冻起来。
2、在 Confirm 阶段,执行近程调用的转账的操作,转账胜利进行冻结。
3、如果第 2 步执行胜利,那么转账胜利,如果第二步执行失败,则调用近程解冻接口对应的冻结办法 (Cancel)。

长处: 跟 2PC 比起来,实现以及流程绝对简略了一些,但数据的一致性比 2PC 也要差一些

毛病: 毛病还是比拟显著的,在 2,3 步中都有可能失败。TCC 属于应用层的一种弥补形式,所以须要程序员在实现的时候多写很多弥补的代码,在一些场景中,一些业务流程可能用 TCC 不太好定义及解决。

三、本地音讯表(异步确保)

本地音讯表这种实现形式应该是业界应用最多的,其核心思想是将分布式事务拆分老本地事务进行解决,这种思路是来源于 ebay。咱们能够从上面的流程图中看出其中的一些细节:

基本思路就是:

音讯生产方,须要额定建一个音讯表,并记录音讯发送状态。音讯表和业务数据要在一个事务里提交,也就是说他们要在一个数据库外面。而后音讯会通过 MQ 发送到音讯的生产方。如果音讯发送失败,会进行重试发送。

音讯生产方,须要解决这个音讯,并实现本人的业务逻辑。此时如果本地事务处理胜利,表明曾经解决胜利了,如果解决失败,那么就会重试执行。如果是业务下面的失败,能够给生产方发送一个业务弥补音讯,告诉生产方进行回滚等操作。

生产方和生产方定时扫描本地音讯表,把还没解决实现的音讯或者失败的音讯再发送一遍。如果有靠谱的主动对账补账逻辑,这种计划还是十分实用的。

这种计划遵循 BASE 实践,采纳的是最终一致性,笔者认为是这几种计划外面比拟适宜理论业务场景的,即不会呈现像 2PC 那样简单的实现(当调用链很长的时候,2PC 的可用性是非常低的),也不会像 TCC 那样可能呈现确认或者回滚不了的状况。

长处: 一种十分经典的实现,防止了分布式事务,实现了最终一致性。在 .NET 中 有现成的解决方案。

毛病: 音讯表会耦合到业务零碎中,如果没有封装好的解决方案,会有很多杂活须要解决。

四、MQ 事务音讯

有一些第三方的 MQ 是反对事务音讯的,比方 RocketMQ,他们反对事务音讯的形式也是相似于采纳的二阶段提交,然而市面上一些支流的 MQ 都是不反对事务音讯的,比方 RabbitMQ 和 Kafka 都不反对。

以阿里的 RocketMQ 中间件为例,其思路大抵为:

第一阶段 Prepared 音讯,会拿到音讯的地址。
第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去拜访音讯,并批改状态。

也就是说在业务办法内要想音讯队列提交两次申请,一次发送音讯和一次确认音讯。如果确认音讯发送失败了 RocketMQ 会定期扫描音讯集群中的事务音讯,这时候发现了 Prepared 音讯,它会向音讯发送者确认,所以生产方须要实现一个 check 接口,RocketMQ 会依据发送端设置的策略来决定是回滚还是持续发送确认音讯。这样就保障了音讯发送与本地事务同时胜利或同时失败。

遗憾的是,RocketMQ 并没有 .NET 客户端。

长处: 实现了最终一致性,不须要依赖本地数据库事务。

毛病: 实现难度大,支流 MQ 不反对,没有.NET 客户端,RocketMQ 事务音讯局部代码也未开源。

五、Sagas 事务模型

Saga 事务模型又叫做长时间运行的事务(Long-running-transaction), 它是由普林斯顿大学的 H.Garcia-Molina 等人提出,它形容的是另外一种在没有两阶段提交的的状况下解决分布式系统中简单的业务事务问题。

咱们这里说的是一种基于 Sagas 机制的工作流事务模型,这个模型的相干实践目前来说还是比拟新的,以至于百度上简直没有什么相干材料。

该模型其核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,而后由 Sagas 工作流引擎负责协调,如果整个流程失常完结,那么就算是业务胜利实现,如果在这过程中实现失败,那么 Sagas 工作流引擎就会以相同的顺序调用弥补操作,从新进行业务回滚。

比方咱们一次对于购买游览套餐业务操作波及到三个操作,他们别离是预约车辆,预约宾馆,预约机票,他们别离属于三个不同的近程接口。可能从咱们程序的角度来说他们不属于一个事务,然而从业务角度来说是属于同一个事务的。

他们的执行程序如上图所示,所以当产生失败时,会顺次进行勾销的弥补操作。

因为长事务被拆分了很多个业务流,所以 Sagas 事务模型最重要的一个部件就是工作流或者你也能够叫流程管理器(Process Manager),工作流引擎和 Process Manager 尽管不是同一个货色,然而在这里,他们的职责是雷同的。在抉择工作流引擎之后,最终的代码兴许看起来是这样的

SagaBuilder saga = SagaBuilder.newSaga("trip")
        .activity("Reserve car", ReserveCarAdapter.class) 
        .compensationActivity("Cancel car", CancelCarAdapter.class) 
        .activity("Book hotel", BookHotelAdapter.class) 
        .compensationActivity("Cancel hotel", CancelHotelAdapter.class) 
        .activity("Book flight", BookFlightAdapter.class) 
        .compensationActivity("Cancel flight", CancelFlightAdapter.class) 
        .end()
        .triggerCompensationOnAnyError();

camunda.getRepositoryService().createDeployment() 
        .addModelInstance(saga.getModel()) 
        .deploy();

优缺点这里咱们就不说了,因为这个实践比拟新,目前市面上还没有什么解决方案,即便是 Java 畛域,我也没有搜寻的太多有用的信息。

分布式事务解决方案:CAP

下面介绍的那些分布式事务的解决计划你在其余中央或者也能够看到,然而并没有相干的理论代码或者是开源代码,所以算不上什么干货,上面就放干货了。

在 .NET 畛域,仿佛没有什么现成的对于分布式事务的解决方案,或者说是有但未开源。具笔者理解,有一些公司外部其实是有这种解决方案的,然而也是作为公司的一个外围产品之一,并未开源 …

鉴于以上起因,所以博主就打算本人写一个并且开源进去,所以从 17 年初就开始做这个事件,而后花了大半年的工夫在始终不断完善,就是上面这个 CAP。

Github CAP:这里的 CAP 就不是 CAP 实践了,而是一个 .NET 分布式事务解决方案的名字。

具体介绍:

http://www.cnblogs.com/savorb…

相干文档:

http://www.cnblogs.com/savorb…

夸大的是,这个解决方案是具备可视化界面(Dashboard)的,你能够很方面的看到哪些音讯执行胜利,哪些音讯执行失败,到底是发送失败还是解决失败,一眼便知。

最夸大的是,这个解决方案的可视化界面还提供了 实时动静图表,这样岂但能够看到实时的音讯发送及解决状况,连以后的零碎解决音讯的速度都能够看到,还能够看到过来 24 小时内的历史音讯吞吐量。

最最夸大的是,这个解决方案的还帮你集成了 Consul 做分布式节点发现和注册还有心跳查看,你随时能够看到其余的节点的情况。

最最最夸大的是,你认为你看其余节点的数据要登录到其余节点的 Dashboard 控制台看?错了,你轻易关上其中任意一个节点的 Dashboard,点一下就能够切换到你想看的节点的控制台界面了,就像你看本地的数据一样,他们是齐全去中心化的。

你认为这些就够了?不,远远不止:

  • CAP 同时反对 RabbitMQ,Kafka 等音讯队列
  • CAP 同时反对 SQL Server, MySql, PostgreSql 等数据库
  • CAP Dashboard 同时反对中文和英文界面双语言,妈妈再也不必放心我看不懂了
  • CAP 提供了丰盛的接口能够供扩大,什么序列化了,自定义解决了,自定义发送了通通不在话下
  • CAP 基于 MIT 开源,你能够只管拿去做二次开发。(记得保留 MIT 的 License)

这下你认为我说完了?不!另外,MQ 系列面试题和答案全副整顿好了,微信搜寻​Java 技术栈,在后盾发送:面试,​能够在线浏览。

你齐全能够把 CAP 当做一个 EventBus 来应用,CAP 具备优良的音讯解决能力,不要放心瓶颈会在 CAP,那是永远不可能,因为你随时能够在配置中指定 CAP 解决的音讯应用的过程数,只有你的数据库配置足够高 …

2 号传送门:https://github.com/dotnetcore…

总结

通过本文咱们理解到两个分布式系统的实践,他们别离是 CAP 和 BASE 实践,同时咱们也总结并比照了几种分布式合成计划的优缺点,分布式事务自身是一个技术难题,是没有一种完满的计划应答所有场景的,具体还是要依据业务场景去抉择吧。而后咱们介绍了一种基于本地音讯的的分布式事务解决方案 CAP。

作者:Savorboard

本文地址:http://www.cnblogs.com/savorb…

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿(2021 最新版)

2. 别在再满屏的 if/ else 了,试试策略模式,真香!!

3. 卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.5 重磅公布,光明模式太炸了!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0