前言
系统一旦分布式了之后,通信、缓存、消息、事务、锁、配置、日志、监控、会话等等各种原来单块系统场景下很容易解决的问题,都会变得很复杂,需要引入大量外部的技术。
本文对分布式基础知识点做出一定的总结,适合想要了解分布式或者刚开始接触分布式的程序员阅读。
一、分布式框架
如果要让不同的子系统或者服务之间互相通信,首先必须有一套分布式服务框架。也就是各个服务可以互相感知到对方在哪里,可以发送请求过去,可以通过HTTP或者RPC的方式。
最常见的技术就是dubbo以及spring cloud,当然大厂一般都是自己有服务框架。
二、分布式事务
2.1 事务特性
ACID
A原子性:在事务的执行过程中,要么全部执行成功,要么都不成功。
C一致性:事务在执行前后,不能破坏数据的完整性。一致性更多的说的是通过AID来达到目的,数据应该符合预先的定义和约束,由应用层面来保证,还有的说法是C是强行为了ACID凑出来的。
I隔离性:多个事务之间是互相隔离的,事务之间不能互相干扰,涉及到不同事务的隔离级别的问题。
D持久性:一旦事务提交,数据库中数据的状态就应该是永久性的。
一个数据库的本地事务机制仅仅对落到自己身上的操作起作用,无法干涉对其他数据库的查询操作。所以,数据库自身提供的本地事务机制无法确保业务对多数据源全局操作的可靠性。
基于此,针对多数据源操作提出的分布式事务机制就出现了。
2.2 分布式事务模型
- 事务参与者:例如每个数据库就是一个事务参与者
- 事务协调者:访问多个数据源的服务程序,例如 shopping-service 就是事务协调者
- 资源管理器(Resource Manager, RM):通常与事务参与者同义
- 事务管理器(Transaction Manager, TM):通常与事务协调者同义
2.3 二将军问题和幂等性
一支白军被围困在一个山谷中,山谷的左右两侧是蓝军。困在山谷中的白军人数多于山谷两侧的任意一支蓝军,而少于两支蓝军的之和。若一支蓝军对白军单独发起进攻,则必败无疑;但若两支蓝军同时发起进攻,则可取胜。两只蓝军的总指挥位于山谷左侧,他希望两支蓝军同时发起进攻,这样就要把命令传到山谷右侧的蓝军,以告知发起进攻的具体时间。假设他们只能派遣士兵穿越白军所在的山谷(唯一的通信信道)来传递消息,那么在穿越山谷时,士兵有可能被俘虏。
网络二将军问题的存在使得消息的发送者往往要重复发送消息,直到收到接收者的确认才认为发送成功,但这往往又会导致消息的重复发送。例如电商系统中订单模块调用支付模块扣款的时候,如果网络故障导致二将军问题出现,扣款请求重复发送,产生的重复扣款结果显然是不能被接受的。因此要保证一次事务中的扣款请求无论被发送多少次,接收方有且只执行一次扣款动作,这种保证机制叫做接收方的幂等性。
2.4 CAP 定理
一个系统不可能同时满足一致性(C:Consistency),可用性(A: Availability)和分区容错性(P:Partition tolerance),最多只能同时满足其中的2个。分布式系统必须满足分区容错性,剩下一个是从一致性和可用性之间做权衡,但是最终都要达到一致性。
Eureca:满足AP
Zookeeper:满足CP
2.5 Base 理论
全称:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)
即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
2.6 XA
分布式事务处理的规范,是一个规范或者说是协议,定义了事务管理器TM(Transaction Manager),资源管理器RM(Resource Manager)和应用程序。
XA定义了规范,那么2PC和3PC就是它的具体实现方式。
2.7 二阶段提交 2PC
Two-Phase Commit,事务的提交过程分成了两个阶段来进行处理。
投票阶段
TM向所有的参与者发送prepare请求,询问是否可以执行事务,等待各个参与者的响应。
这个阶段可以认为只是执行了事务的SQL语句,但是还没有提交。
如果都执行成功了就返回YES,否则返回NO。
执行阶段
执行阶段就是真正的事务提交的阶段,但是要考虑到失败的情况。
如果所有的参与者都返回YES,那么就执行发送commit命令,参与者收到之后执行提交事务。
反之,只要有任意一个参与者返回的是NO的话,就发送rollback命令,然后执行回滚的操作。
2PC的缺陷
1.同步阻塞,可以看到,在执行事务的过程当中,所有数据库的资源都被锁定,如果这时候有其他人来访问这些资源,将会被阻塞,这是一个很大的性能问题。
2.TM单点故障问题,只要一个TM,一旦TM宕机,那么整个流程无法继续完成。
3.数据不一致,如果在执行阶段,参与者脑裂或者其他故障导致没有收到commit请求,部分提交事务,部分未提交,那么数据不一致的问题就产生了。
2.8 三阶段提交 3PC
既然2PC有这么多问题,所以就衍生出了3PC的概念,也叫做三阶段提交,他把整个流程分成了CanCommit、PreCommit、DoCommit三个步骤,相比2PC,增加的就是CanCommit阶段。
与两阶段提交不同的是,三阶段提交有两个改动点。
1.引入超时机制。同时在协调者和参与者中都引入超时机制。
2.在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。
相比2PC的改进
对于2PC的同步阻塞的问题,我们可以看到因为3PC加入了参与者的超时机制,所以原来2PC的如果某个参与者故障导致的同步阻塞的问题时间缩短了,这是一个优化,但是并没有完全避免。
第二个单点故障的问题,同样因为超时机制的引入,一定程度上也算是优化了。
但是数据不一致的问题,这个始终没有得到解决。
2.9 TCC
TCC的模式叫做Try、Confirm、Cancel,实际上也就是2PC的一个变种而已。
实现这个模式,一个事务的接口需要拆分成3个,也就是Try预占、Confirm确认提交、最后Cancel回滚。
几个比较不错的框架,都是国内开源的:ByteTCC,tcc-transaction,himly
2.10 消息队列
以上都是一些理论,生产环境中很少有人用到。而基于消息队列来实现最终一致性的方案,这个相比前面的我个人认为还稍微靠谱一点。基于消息队列的可能真正在应用的还稍微多一点。
这个方案基于MQ来保证消息事务的最终一致性,还算是一个比较合理的解决方案,只要保证MQ的可靠性就可以正常实施应用,业务消费方根据本身的消息重试达到最终一致性。
参考文献:
[1].Java同学找工作最懵圈的问题:到底啥是分布式系统开发经验?
[2].《我想进大厂》之分布式事务篇
[3].搞定分布式,程序员进阶之路
[5].分布式相关面试题