架构

提到分布式架构就一定绕不开“一致性”问题,分布式的理论必须要了解的点包括ACID、CAP、BASE、强一致性、弱一致性、最终一致性。

主要分享的内容包括

1.了解分布式事务产生的原因
2.知道几种分布式事务解决方案:XA、TCC、消息事务、TA、SAGA
3.知道分布式事务各种解决方案的优缺点和使用场景

分布式事务产生的原因

要了解分布式事务产生的原因,先来说说本地事务
事务是指由一系列严密的操作组成的一个完整的逻辑过程,这个过程中的所有操作要么都成功,要么都不成功。而本地事务,是指传统的单机数据库事务,必须具备ACID原则
本地事务ACID原则
ACID是事务的四个特性,指的是atomicity,原子性;consistency,一致性;isolation,隔离性;durability,持久性。

  1. 原子性(atomicity): 指所有在事务中的操作要么都成功,要么都不成功,所有的操作都不可分割,没有中间状态。一旦某一步执行失败,就会全部回滚到初始状态。
  2. 一致性(consistency): 指的是逻辑上的一致性,即所有操作是符合现实当中的期望的。
  3. 隔离性(isolation): 即不同事务之间的相互影响和隔离的程度。比如,不同的隔离级别,事务的并发程度也不同,最强的隔离状态是所有的事务都是串行化的(serializable)(即一个事务完成之后才能进行下一个事务),这样并发性也会降到最低,在保证了强一致性的情况下,性能也会受很大影响,所以在实际工程当中,往往会折中一下。
  4. 持久性(durability): 可以简单地理解为事务执行完毕后数据不可逆并持久化存储于存储系统当中

满足ACID原则的一个逻辑操作就可以成为一个本地事务。
分布式事务
分布式事务分布式事务,就是指不是在单个服务或单个数据库架构下,产生的事务:
跨数据源的分布式事务
跨服务的分布式事务
两种情况交错的复杂情况
跨数据源
随着业务数据规模的快速发展,数据量越来越大,单库单表逐渐成为瓶颈。所以我们对数据库进行了水平拆分,将原单库单表拆分成数据库分片,于是就产生了跨数据库事务问题。
image-20200304201018438.png
跨服务
随着业务的快速发展,系统的访问量和业务复杂程度都在快速增长,单系统架构逐渐成为业务发展瓶颈,按照面向服务(SOA)的架构的设计原则,将单业务系统拆分成多个业务系统,降低了各系统之间的耦合度,使不同的业务系统专注于自身业务,更有利于业务的发展和系统容量的伸缩
image-20200304202639509.png
分布式事务一致性问题
1.单个本地事务可以保证ACID原则
2.多个服务之间的“业务”的原子性保证,要么全部成功要么全部失败,这就是分布式系统下的事务了。
3.此时ACID难以满足,这是分布式事务要解决的问题
image-20200304204442839.png
比如:订单生成了,库存也扣减了,但是用户账户的余额不足,这就造成数据不一致。
订单的创建、库存的扣减、账户扣款在每一个服务和数据库内是一个本地事务,可以保证ACID原则。但是当我们把三件事情看做一个事情事,要满足保证“业务”的原子性,要么所有操作全部成功,要么全部失败,这就要需要考虑使用分布式事务来解决数据一致性问题的场景了。

解决分布式系统的思路

CAP定理
Base理论

CAP定理

本小节内容摘自:CAP 定理的含义
什么是CAP定理呢?
image-20200304205842784.png
1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标。

  • Consistency(一致性)
  • Availability(可用性)
  • Partition tolerance (分区容错性)

它们的第一个字母分别是 C、A、P。
Eric Brewer 说,这三个指标不可能同时做到。这个结论就叫做 CAP 定理。
Partition tolerance
先看 Partition tolerance,中文叫做"分区容错"。
大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在上海,另一台服务器放在北京,这就是两个区,它们之间可能因网络问题无法通信。
如图:image-20200304210120471.png

上图中,G1 和 G2 是两台跨区的服务器。G1 向 G2 发送一条消息,G2 可能无法收到。系统设计的时候,必须考虑到这种情况。
一般来说,分布式系统,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。根据CAP 定理,剩下的 C 和 A 无法同时做到。
Consistency
Consistency 中文叫做"一致性"。意思是,写操作之后的读操作,必须返回该值。举例来说,某条记录是 v0,用户向 G1 发起一个写操作,将其改为 v1。
image-20200304210414309.png
接下来,用户的读操作就会得到 v1。这就叫一致性。
image-20200304210506575.png
问题是,用户有可能向 G2 发起读操作,由于 G2 的值没有发生变化,因此返回的是 v0。G1 和 G2 读操作的结果不一致,这就不满足一致性了。
image-20200304210521364.png
为了让 G2 也能变为 v1,就要在 G1 写操作的时候,让 G1 向 G2 发送一条消息,要求 G2 也改成 v1。
image-20200304210540168.png
image-20200304210557117.png
这样的话,用户向 G2 发起读操作,也能得到 v1。
Availability
Availability 中文叫做"可用性",意思是只要收到用户的请求,服务器就必须给出回应(对和错不论)。
用户可以选择向 G1 或 G2 发起读操作。不管是哪台服务器,只要收到请求,就必须告诉用户,到底是 v0 还是 v1,否则就不满足可用性。
Consistency 和 Availability 的矛盾
一致性和可用性,为什么不可能同时成立?
答案很简单,因为可能通信失败(即出现分区容错)。
如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,没有可用性不。
如果保证 G2 的可用性,那么势必不能锁定 G2,所以一致性不成立。
综上所述,G2 无法同时做到一致性和可用性。系统设计时只能选择一个目标。如果追求一致性,那么无法保证所有节点的可用性;如果追求所有节点的可用性,那就没法做到一致性。

Base理论

BASE是三个单词的缩写:

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

而我们解决分布式事务,就是根据上述理论来实现。
还以上面的下单减库存和扣款为例:
订单服务、库存服务、用户服务及他们对应的数据库就是分布式应用中的三个部分。

  • CP方式:现在如果要满足事务的强一致性,就必须在订单服务数据库锁定的同时,对库存服务、用户服务数据资源同时锁定。等待三个服务业务全部处理完成,才可以释放资源。此时如果有其他请求想要操作被锁定的资源就会被阻塞,这样就是满足了CP。

这就是强一致,弱可用

  • AP方式:三个服务的对应数据库各自独立执行自己的业务,执行本地事务,不要求互相锁定资源。但是这个中间状态下,我们去访问数据库,可能遇到数据不一致的情况,不过我们需要做一些后补措施,保证在经过一段时间后,数据最终满足一致性。

这就是高可用,但弱一致(最终一致)。
由上面的两种思想,延伸出了很多的分布式事务解决方案:

  • XA 方案
  • TCC 方案
  • SAGA 方案
  • 本地消息表
  • 可靠消息最终一致性方案
  • 最大努力通知方案

本文先说下XA(二阶段提交),和TCC方案,其它内容后续会继续更新。

二阶段提交/XA方案(2PC)

所谓的XA方案,即:两阶段提交,二阶段提交引入一个事务协调者角色来协调管理各个参与者的提交和回滚。
二阶段分别指:准备(投票),提交两个阶段

第一阶段:所有事务参与者,执行后进行预提交,也就是预留事务所需的资源;直到协调者收到所有参与者的预提交才会进入第二步;

  • 如果在协调者的超时时间内,有任意参与者的预提交preCommit没发送或未到达,都会结束事务。

第二阶段:所有事务预提交了各自的结果后,由协调者决定最终事务是成功(commit)还是失败(rollback)。
准备阶段
1625047486(1).jpg
提交阶段
1625047521(1).jpg
二阶段提交的问题

1.执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
2.参与者发生故障。协调者需要给每个参与者额外指定超时机制,超时后整个事务失败。(没有多少容错机制)。
3.二阶段无法解决的问题:协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交,事务容易处于悬而未决的状态。
4.效率比较低,不适合高并发场景

Spring Boot + JTA 可以实现二阶段提交:
参考地址:https://www.hifreud.com/2017/07/12/spring-boot-23-jta-handle-distribute-transaction/

TCC(Try-Confirm-Cancel)

TCC 三个方法描述:

  • Try:资源的检测和预留(锁定);
  • Confirm:执行实际的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
  • Cancel:如果任一服务执行出错,那么进行补偿,业务回滚,预留资源释放;

image-20200305155521612.png

TCC优缺点

•优势
TCC执行的每一个阶段都会提交本地事务并释放锁,并不需要等待其它事务的执行结果。而如果其它事务执行失败,最后不是回滚,而是执行补偿操作。这样就避免了资源的长期锁定和阻塞等待,执行效率比较高,属于性能比较好的分布式事务方式。

•缺点
代码侵入:需要人为编写代码实现try、confirm、cancel,代码侵入较多
开发成本高:一个业务需要拆分成3个步骤,分别编写业务实现,业务编写比较复杂
安全性考虑:cancel动作如果执行失败,资源就无法释放,需要引入重试机制,而重试可能导致重复执行,还要考虑重试时的幂等问题

TCC使用场景
•对事务有一定的一致性要求(最终一致)
•对性能要求较高
•开发人员具备较高的编码能力和幂等处理经验

内容之外的一个经典又有趣的问题。

二将军问题(Two Generals Problem)

1625048475(1).jpg
如图所示,白军驻扎在沟渠里,蓝军则分散在沟渠两边。白军比任何一支蓝军都更为强大,但是蓝军若能同时合力进攻则能够打败白军。他们不能够远程的沟通,只能派遣通信兵穿过沟渠去通知对方蓝军协商进攻时间。是否存在一个能使蓝军必胜的通信协议,这就是两军问题。
必须注意的是,通信兵得经过敌人的沟渠,在这过程中他可能被捕,也就是说,两军问题中信道是不可靠的,并且其中没有叛徒之说.
倘若1号蓝军(简称1)向2号蓝军(简称2)派出了通信兵,若1要知道2是否收到了自己的信息,1必须要求2给自己传输一个回执,说“你的信息我已经收到了,我同意你提议的明天早上10点9分准时进攻”。
然而,就算2已经送出了这条信息,2也不能确定1就一定会在这个时间进攻,因为2发出的回执1并不一定能够收到。所以,1必须再给2发出一个回执说“我收到了”,但是1也不会知道2是否收到了这样一个回执,所以1还会期待一个2的回执。

二将军问题很好的说明了网络传输是不可靠的。

总结-什么是分布式事务

在一个分布式系统中各个数据节点之间数据的ACID特性保证.
通过事务拆分,和事件补偿的方法来保证分布式事务的一致性。
解决方案:
拆分:
1.分布式事务-》长事务
2.本地事务-》短事务
3.长事务拆分多个短事务
补偿:
A->B->C
A,B成功,C失败
•C不需要补偿
•补偿B,A

Comment

This is just a placeholder img.