分布式系统是一个硬件或软件组件分布在不同计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。
CAP理论
对于一个分布式系统而言,无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)。
一致性
对某个指定的客户端来说,读操作保证能够返回最新的写操作结果,强调数据正确
可用性
非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
强调的是服务可用,但不保证数据正确。
分区容错性
当节点间出现任意数量的消息丢失或高延迟(网络分区)的时候,系统仍然在继续工作。
在不存在网络分区的情况下,也就是分布式系统正常运行时(这也是系统在绝大部分时候所处的状态),就是说在不需要 P 时,C 和 A 能够同时保证。只有当发生分区故障的时候,也就是说需要 P 时,才会在 C 和 A 之间做出选择。而且如果读操作会读到旧数据,影响到了系统运行或业务运行(也就是说会有负面的影响),推荐选择 C,否则选 A。
ACID与分布式事务
ACID
事务需要具备的4个基本特性:
- 原子性(Atomicity):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
- 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
- 事务隔离(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
分布式系统涉及多个节点间的操作。加锁、时间序列等机制,只能保证单个节点上操作的 ACID 特性,无法保证节点间操作的 ACID 特性。
分布式事务
2PC(二阶段提交协议)
角色: 协调者+参与者
- 第一阶段(提交请求,又称投票阶段)
- 第二阶段(提交执行阶段,又称完成阶段)
二阶段提交协议最早是用来实现数据库的分布式事务的,不过现在最常用的协议是 XA 协议。这个协议是 X/Open 国际联盟基于二阶段提交协议提出的,也叫作 X/Open Distributed Transaction Processing(DTP)模型,比如 MySQL 就是通过 MySQL XA 实现了分布式事务。
- 优点:尽量保证了数据的强一致性
- 缺点:
- 单点故障问题,协调者挂了,尤其在第二阶段的时候参与者资源都锁着时候影响更大。
- 性能问题: 所有节点在执行时是同步阻塞的
- 数据不一致问题,在第二个阶段,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就会导致节点间数据的不一致问题。
3PC(三阶段提交协议)
在两阶段提交的基础上增加了CanCommit阶段,并引入了超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效地解决了协调者单点故障的问题。
- CanCommit: 只负责对操作条件的校验,不做其它
- PreCommit: 负责锁资源、执行、写redo和undo日志,但不提交
- DoCommit: 正常完成操作,释放资源
从两阶段提交到三阶段提交,通过拆分了prepare后,可以在cancommit阶段就能最大概率快速确定该次事务是否能够执行。一旦所有参与者都返回yes后,才通知各个参与者锁定资源;这样就避免了两阶段提交中,一上来各自就开始锁定资源,然后发现某个参与者有问题后又通知各自在rollback操作中去释放资源的补救措施。
优点:
- 参与者超时机制,不会再傻等。
- 增加询问阶段不会直接锁资源
- 解决协调者和个别参与者一起挂了之后,选择上线不知该提交还是回滚的问题
缺点
- 多了一步、耗时更多
- 网络分区情况下还是有数据不一致问题
TCC(Try-Confirm-Cancel)
TCC 是 Try(预留)、Confirm(确认)、Cancel(撤销) 3 个操作的简称,它包含了预留、确认或撤销这 2 个阶段。
TCC 本质上是补偿事务,它的核心思想是针对每个操作都要注册一个与其对应的确认操作和补偿操作(也就是撤销操作)。 它是一个业务层面的协议,你也可以将 TCC 理解为编程模型,TCC 的 3 个操作是需要在业务代码中编码实现的,为了实现一致性,确认操作和补偿操作必须是等幂的,因为这 2 个操作可能会失败重试。另外,TCC 不依赖于数据库的事务,而是在业务中实现了分布式事务,这样能减轻数据库的压力,但对业务代码的入侵性也更强,实现的复杂度也更高
优点:
- 基于业务,不需要占用阻塞数据库宝贵资源
- 基于业务也更加灵活
缺点:
BASE理论
- 基本可用(Basically Available):分布式系统在出现不可预知故障时,允许牺牲部分可用性(如响应时间/功能降级等)
- 软状态(Soft State):允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性
- 最终一致性(Eventually Consistent):系统中所有的数据副本,在经过一段时间的同步后,最终能够到达一个一致的状态。
基本可用实现方法:
- 流量错峰(不同地区售票时间错峰出售)
- 异步处理(买票排队,基于队列先收到用户买票请求,排队异步处理,延迟响应)
- 服务降级(看到非实时数据,采用缓存数据提供服务)
- 过载保护(熔断/限流,直接拒绝掉一部分请求,或者当请求队列满了,移除一部分请求,保证整体系统可用)
- 故障隔离(出现故障,做到故障隔离,避免影响其他服务)
- 弹性扩容(基于Metric和Monitor实现系统态势感知,做到弹性伸缩)
分布式协议与算法
拜占庭容错
拜占庭将军问题(Byzantine Generals Problem),是由莱斯利·兰波特在其同名论文中提出的分布式对等网络通信容错问题。
在分布式计算中,不同的计算机通过通讯交换信息达成共识而按照同一套协作策略行动。但有时候,系统中的成员计算机可能出错而发送错误的信息,用于传递信息的通讯网络也可能导致信息损坏,使得网络中不同的成员关于全体协作的策略得出不同结论,从而破坏系统一致性
核心点: 除了存在故障行为,还存在恶意行为
拜占庭容错(Byzantine Fault Tolerance,BFT),就是指能容忍拜占庭错误了。而非拜占庭容错,又叫故障容错(Crash Fault Tolerance,CFT),解决的是分布式系统中存在故障,但不存在恶意节点的共识问题,比如进程崩溃、服务器硬件故障等。
- 一般而言,在可信环境(比如企业内网)中,系统具有故障容错能力就可以了,常见的算法有二阶段提交协议(2PC)、TCC(Try-Confirm-Cancel)、Paxos 算法、ZAB 协议、Raft 算法、Gossip 协议、Quorum NWR 算法。
- 而在不可信的环境(比如有人做恶)中,这时系统需要具备拜占庭容错能力,常见的拜占庭容错算法有 POW 算法、PBFT 算法。
3种一致性
- 强一致性:保证写操作完成后,任何后续访问都能读到更新后的值。
- 弱一致性:写操作完成后,系统不能保证后续的访问都能读到更新后的值。
- 最终一致性:保证如果对某个对象没有新的写操作了,最终所有后续访问都能读到相同的最近更新的值。
Paxos、Raft 能实现线性一致性,而 ZooKeeper 基于读性能的考虑,它通过 ZAB 协议提供的是最终一致性。
共识与一致性
共识(Consensus)和一致性(Consistency)是两个完全不同的概念。
- 共识:各节点就指定值(Value)达成共识,而且达成共识后的值,就不再改变了。
- 一致性:是指写操作完成后,能否从各节点上读到最新写入的数据,如果立即能读到,就是强一致性,如果最终能读到,就是最终一致性。
参考资料: