分布式系统基本原理
📦 本文已归档到:「blog」
1. 分布式术语
1.1. 异常
服务器宕机
- 内存错误、服务器停电等都会导致服务器宕机,此时节点无法正常工作,称为不可用。服务器宕机会导致节点失去所有内存信息,因此需要将内存信息保存到持久化介质上。网络异常
- 有一种特殊的网络异常称为——网络分区 ,即集群的所有节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信。磁盘故障
- 磁盘故障是一种发生概率很高的异常。使用冗余机制,将数据存储到多台服务器。
1.2. 超时
在分布式系统中,一个请求除了成功和失败两种状态,还存在着超时状态。
可以将服务器的操作设计为具有 幂等性 ,即执行多次的结果与执行一次的结果相同。如果使用这种方式,当出现超时的时候,可以不断地重新请求直到成功。
1.3. 衡量指标
性能
常见的性能指标有:吞吐量、响应时间。
其中,吞吐量指系统在某一段时间可以处理的请求总数,通常为每秒的读操作数或者写操作数;响应时间指从某个请求发出到接收到返回结果消耗的时间。
这两个指标往往是矛盾的,追求高吞吐的系统,往往很难做到低响应时间,解释如下:
在无并发的系统中,吞吐量为响应时间的倒数,例如响应时间为 10 ms,那么吞吐量为 100 req/s,因此高吞吐也就意味着低响应时间。
但是在并发的系统中,由于一个请求在调用 I/O 资源的时候,需要进行等待。服务器端一般使用的是异步等待方式,即等待的请求被阻塞之后不需要一直占用 CPU 资源。这种方式能大大提高 CPU 资源的利用率,例如上面的例子中,单个请求在无并发的系统中响应时间为 10 ms,如果在并发的系统中,那么吞吐量将大于 100 req/s。因此为了追求高吞吐量,通常会提高并发程度。但是并发程度的增加,会导致请求的平均响应时间也增加,因为请求不能马上被处理,需要和其它请求一起进行并发处理,响应时间自然就会增高。
可用性
可用性指系统在面对各种异常时可以提供正常服务的能力。可以用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。
一致性
可以从两个角度理解一致性:从客户端的角度,读写操作是否满足某种特性;从服务器的角度,多个数据副本之间是否一致。
可扩展性
指系统通过扩展集群服务器规模来提高性能的能力。理想的分布式系统需要实现“线性可扩展”,即随着集群规模的增加,系统的整体性能也会线性增加。
2. 分布式基础理论
:bulb: CAP 和 BASE 详尽剖析,请参考:CAP 定理和 BASE 理论
3. 分布式事务
3.1. 两阶段提交(2PC)
两阶段提交(Two-phase Commit,2PC)
主要用于实现分布式事务,分布式事务指的是事务操作跨越多个节点,并且要求满足事务的 ACID 特性。
通过引入协调者(Coordinator)来调度参与者的行为,并最终决定这些参与者是否要真正执行事务。
运行过程
准备阶段
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。

提交阶段
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。

需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
问题
同步阻塞
所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
单点问题
协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响,特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
数据不一致
在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
太过保守
任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
PC 优缺点
优点:尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能 100%保证强一致)
缺点:实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景。
3.2. 补偿事务(TCC)
补偿事务(TCC)其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:
- Try 阶段主要是对业务系统做检测及资源预留。
- Confirm 阶段主要是对业务系统做确认提交,Try 阶段执行成功并开始执行 Confirm 阶段时,默认 Confirm 阶段是不会出错的。即:只要 Try 成功,Confirm 一定成功。
- Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
举个例子,假设 Bob 要向 Smith 转账,思路大概是:
- 首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
- 在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
- 如果第 2 步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。
TCC 优缺点
- 优点:跟 2PC 比起来,实现以及流程相对简单了一些,但数据的一致性比 2PC 也要差一些。
- 缺点:缺点还是比较明显的,在 2,3 步中都有可能失败。TCC 属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用 TCC 不太好定义及处理。
3.3. 本地消息表(异步确保)
本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性。
- 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。
- 之后将本地消息表中的消息转发到 Kafka 等消息队列(MQ)中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。
- 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。

这种方案遵循 BASE 理论,采用的是最终一致性。
本地消息表利用了本地事务来实现分布式事务,并且使用了消息队列来保证最终一致性。
本地消息表优缺点
- 优点:一种非常经典的实现,避免了分布式事务,实现了最终一致性。
- 缺点:消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
3.4. MQ 事务消息
有一些第三方的 MQ 是支持事务消息的,比如 RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交。但是市面上一些主流的 MQ 都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中间件为例,其思路大致为:
- Prepared 消息,会拿到消息的地址。
- 执行本地事务。
- 通过第一阶段拿到的地址去访问消息,并修改状态。
也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了 RocketMQ 会定期扫描消息集群中的事务消息,这时候发现了 Prepared 消息,它会向消息发送者确认,所以生产方需要实现一个 check 接口,RocketMQ 会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
MQ 事务消息优缺点
- 优点:实现了最终一致性,不需要依赖本地数据库事务。
- 缺点:实现难度大,主流 MQ 不支持。
4. 共识性算法
4.1. Paxos
:bulb: Paxos 详尽剖析,请参考:深入剖析共识性算法 Paxos
4.2. Raft
:bulb: Raft 详尽剖析,请参考:深入剖析共识性算法 Raft
5. 参考资料
- 杨传辉. 大规模分布式存储系统: 原理解析与架构实战[M]. 机械工业出版社, 2013.
- 区块链技术指南
- NEAT ALGORITHMS - PAXOS
- Raft: Understandable Distributed Consensus
- Paxos By Example
- 聊聊分布式事务,再说说解决方案