什么是一致性
一致性是计算机领域里的一个专业术语,在不同场景有不同定义。传统的单体架构中,由于数据都保存在同一个节点,这时的一致性是指节点数据的修改对节点内的其他线程可见、一致。随着业务发展,数据量越来越大,单体架构已经无法支撑如此大的并发,这时需要应用 SOA、微服务等思想,将业务模块拆分为多个节点,这时虽然能扛住高并发、大流量的请求,同时也存在不同节点之间的数据交换问题
一致性问题
- 下订单和扣库存
电商系统中的经典案例,保证下订单和扣库存的操作一致。如果先下订单,扣库存失败,则导致超卖;如果下订单不成功,扣库存成功,那么会导致少卖
2.缓存与数据库不一致
在高并发的场景中,对读多的场景不会直接每次都通过访问数据库来获取最新的数据,而是在数据库前增加一层缓存,那么这里缓存和数据库如何保证一致性,强一致性还是弱一致性?
一致性解决方案
CAP 原理
在理论计算机科学中,CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer's theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:
- 一致性(Consistency) (等同于所有节点访问同一份最新的数据副本)
- 可用性(Availability)(每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据)
- 分区容错性(Partition tolerance)(以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择)
根据定理,分布式系统只能满足三项中的两项而不可能满足全部三项。理解CAP理论的最简单方式是想象两个节点分处分区两侧。允许至少一个节点更新状态会导致数据不一致,即丧失了C性质。如果为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了A性质。除非两个节点可以互相通信,才能既保证C又保证A,这又会导致丧失P性质。
BASE 理论
即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
BA(Basically Available) 基本可用,指分布式系统中,如果依赖的其他节点出现故障,也必须能够向外提供服务
响应时间上的损失:正常情况下的搜索引擎0.5秒即返回给用户结果,而基本可用的搜索引擎可以在2秒作用返回结果。
功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单。但是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
S(Soft state) 软状态,指不同分布式节点的数据允许出现中间状态,并认为该状态不影响系统整体的可用性,即允许系统在多个不同节点的数据副本存在数据延时
E(Eventually Consistent) 最终一致性,上面说的软状态,不可能一直是软状态。在期限过后,应当保证所有副本数据保持一致,从而达到数据一致性。这个时间取决于网路延时、系统负载、数据复制方案设计等
小结
总体来说BASE理论面向的是大型高可用、可扩展的分布式系统。与传统ACID特性相反,不同于ACID的强一致性模型,BASE提出通过牺牲强一致性来获得可用性,并允许数据段时间内的不一致,但是最终达到一致状态。同时,在实际分布式场景中,不同业务对数据的一致性要求不一样。因此在设计中,ACID和BASE理论往往又会结合使用。
分布式一致性协议
将分布式节点中的角色分为:参与者、协调者,参与者负责业务处理,协调者用来对所有参与者提交的请求处理结果进行判断,是一种强一致性的协议。
1. 2pc(Two-phase commit protocol)两阶段提交协议。
两阶段提交协议把分布式事务分为两个阶段,一个是准备阶段,另一个是提交阶段。通过增加预提交的操作,来保证事务的一致性
- 准备(prepare commit)阶段通过协调者向所有参与者发送预提交的请求,当其中一个参与出现异常,其他所有的参与者都会执行回滚操作来处理预提交造成的影响。
- 提交(do commit)阶段为当所有参与者都预提交成功之后,协调者向所有参与者发起真正的事务提交操作,这时如果其中有一个参与者提交事务失败的话,其他所有参与者将执行 rollback 回滚操作
缺点
两阶段提交协议在第一次预提交(prepare)的时候将会锁住资源,这是一个重量级的操作,能保证强一致性,但是实现起来复杂、成本较高、不够灵活。
阻塞
从两次操作来看,对于协调者的每次请求,都需要所有参与者请求处理完成之后,才能进入下一步操作,否则处于阻塞状态,此时占用的资源也一直锁定,不会释放单点故障
如果协调者宕机,参与者没有协调者指挥,则会一直阻塞。同样的,若此时有一个参与者因为网络原因,导致响应时间过慢,将会影响所有节点执行的速度协调者与参与者同时故障
当协调者在第二阶段向各参与者发送了 commit 操作后宕机,若此时唯一接收到 commit 请求的参与者也宕机,即使协调者被新选举出来了,这条事务的状态也是不确定的,没人知道事务是否被已经提交
2. 三阶段提交协议(Three-phase commit protocol)
三阶段提交协议是两阶段提交协议的改版,它通过超时机制解决了阻塞问题,并且把 预提交阶段,拆分为2个阶段
询问阶段(can commit)
询问阶段不会执行任何锁定资源的操作,只需要向协调者响应当前是否可以执行本次事务操作即可预提交阶段(prepare commit)
假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。
- 发送预提交请求 协调者向参与者发送PreCommit请求,并进入Prepared阶段。
- 事务预提交 参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。
- 响应反馈 如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。
假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。
- 发送中断请求 协调者向所有参与者发送abort请求。
- 中断事务 参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。
- 提交阶段 (do commit)
该阶段进行真正的事务提交,也可以分为以下两种情况。
执行提交
发送提交请求 协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。
事务提交 参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
响应反馈 事务提交完之后,向协调者发送Ack响应。
完成事务 协调者接收到所有参与者的ack响应之后,完成事务。
中断事务 协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。
发送中断请求 协调者向所有参与者发送abort请求
事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。
反馈结果 参与者完成事务回滚之后,向协调者发送ACK消息
中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。
不同点:
- 增加了一个询问阶段,询问阶段可以尽可能的发现所有参与者的状态是否可执行当前操作,从而可以中断,而不是在预提交或提交阶段发现。越往后发现,代价越高。
- 二阶段提交的 commit 阶段如果发生故障
- 此时如果只是协调者发生故障。可以通过选举一个新的协调者来继续完成 commit 请求即可
- 如果此时协调者和参与者都崩溃,这时新选举的协调者则无法知道到底是参与者拒绝提交还是确认提交
而在三阶段提交中,通过增加询问阶段之后,在 commit 崩溃之后,重新选出协调者,仍可以知道目前至少是处于准备通过提案阶段,表示第一阶段大家都已经决定要通过了,此时便可以认为当前是确认提交。
- 相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会导致数据一致性问题,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。
参考:
https://en.wikipedia.org/wiki/Three-phase_commit_protocol